@leofcoin/chain 1.4.20 → 1.4.22

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +14 -14
  2. package/LICENSE +88 -88
  3. package/README.md +4 -4
  4. package/demo/index.html +25 -25
  5. package/examples/contracts/token.js +7 -7
  6. package/package.json +71 -71
  7. package/plugins/bundle.js +18 -18
  8. package/src/chain.js +716 -716
  9. package/src/config/config.js +14 -14
  10. package/src/config/main.js +4 -4
  11. package/src/config/protocol.js +5 -5
  12. package/src/contract.js +51 -51
  13. package/src/fee/config.js +3 -3
  14. package/src/machine.js +215 -215
  15. package/src/node.js +24 -24
  16. package/src/protocol.js +3 -3
  17. package/src/state.js +31 -31
  18. package/src/transaction.js +233 -233
  19. package/src/type.index.d.ts +20 -20
  20. package/src/typer.js +19 -19
  21. package/test/chain.js +120 -109
  22. package/test/contracts/token.js +40 -40
  23. package/test/create-genesis.js +66 -66
  24. package/tsconfig.js +14 -14
  25. package/workers/block-worker.js +40 -40
  26. package/workers/machine-worker.js +218 -218
  27. package/workers/pool-worker.js +28 -28
  28. package/workers/transaction-worker.js +19 -19
  29. package/workers/workers.js +8 -8
  30. package/block-worker.js +0 -1
  31. package/demo/865.browser.js +0 -10
  32. package/demo/chain.browser.js +0 -66842
  33. package/demo/generate-account.browser.js +0 -50
  34. package/demo/messages.browser.js +0 -328
  35. package/demo/multi-wallet.browser.js +0 -15
  36. package/demo/node.browser.js +0 -9858
  37. package/demo/pako.browser.js +0 -6900
  38. package/demo/peernet-swarm.browser.js +0 -839
  39. package/demo/storage.browser.js +0 -3724
  40. package/demo/workers/865.js +0 -10
  41. package/demo/workers/block-worker.js +0 -13175
  42. package/demo/workers/machine-worker.js +0 -13385
  43. package/demo/workers/pool-worker.js +0 -8503
  44. package/demo/workers/transaction-worker.js +0 -8495
  45. package/demo/wrtc.browser.js +0 -28
  46. package/dist/browser/workers/865.js +0 -10
  47. package/dist/browser/workers/block-worker.js +0 -9460
  48. package/dist/browser/workers/machine-worker.js +0 -9670
  49. package/dist/browser/workers/pool-worker.js +0 -4608
  50. package/dist/browser/workers/transaction-worker.js +0 -4600
  51. package/dist/chain.js +0 -10128
  52. package/dist/client-80bc8156.js +0 -491
  53. package/dist/commonjs-7fe3c381.js +0 -270
  54. package/dist/contracts/factory.js +0 -1
  55. package/dist/contracts/name-service.js +0 -1
  56. package/dist/contracts/native-token.js +0 -1
  57. package/dist/contracts/validators.js +0 -1
  58. package/dist/generate-account-445db122.js +0 -46
  59. package/dist/index-57f93805.js +0 -718
  60. package/dist/messages-bce1b91d-81af3b00.js +0 -315
  61. package/dist/module/chain.js +0 -10091
  62. package/dist/module/client-8031ec88.js +0 -489
  63. package/dist/module/commonjs-9005d5c0.js +0 -268
  64. package/dist/module/generate-account-489552b6.js +0 -44
  65. package/dist/module/index-ac2285c4.js +0 -688
  66. package/dist/module/messages-bce1b91d-eaf75d83.js +0 -302
  67. package/dist/module/node.js +0 -7049
  68. package/dist/module/workers/block-worker.js +0 -94
  69. package/dist/module/workers/machine-worker.js +0 -304
  70. package/dist/module/workers/pool-worker.js +0 -55
  71. package/dist/module/workers/transaction-worker.js +0 -47
  72. package/dist/node.js +0 -7061
  73. package/dist/standards/token.js +0 -1
  74. package/dist/workers/machine-worker.js +0 -1
  75. package/dist/workers/pool-worker.js +0 -1
  76. package/dist/workers/transaction-worker.js +0 -1
package/src/chain.js CHANGED
@@ -1,716 +1,716 @@
1
- import { BigNumber, formatUnits, parseUnits, formatBytes } from '@leofcoin/utils'
2
- import Machine from './machine.js'
3
- import { ContractMessage, TransactionMessage, BlockMessage, BWMessage, BWRequestMessage } from '@leofcoin/messages'
4
- import addresses, { nativeToken } from '@leofcoin/addresses'
5
- import { contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage, calculateFee } from '@leofcoin/lib'
6
- import State from './state.js'
7
- import Contract from './contract.js'
8
-
9
- globalThis.BigNumber = BigNumber
10
-
11
- // check if browser or local
12
- export default class Chain extends Contract {
13
- /** {Address[]} */
14
- #validators = []
15
- /** {Block[]} */
16
- #blocks = []
17
-
18
- #machine
19
- /** {Boolean} */
20
- #runningEpoch = false
21
-
22
- /** {Boolean} */
23
- #chainSyncing = false
24
-
25
- /** {Number} */
26
- #totalSize = 0
27
-
28
- /**
29
- * {Block} {index, hash, previousHash}
30
- */
31
- #lastBlock = {index: 0, hash: '0x0', previousHash: '0x0'}
32
-
33
- /**
34
- * amount the native token has been iteracted with
35
- */
36
- #nativeCalls = 0
37
-
38
- /**
39
- * amount the native token has been iteracted with
40
- */
41
- #nativeTransfers = 0
42
-
43
- /**
44
- * amount of native token burned
45
- * {Number}
46
- */
47
- #nativeBurns = 0
48
-
49
- /**
50
- * amount of native tokens minted
51
- * {Number}
52
- */
53
- #nativeMints = 0
54
-
55
- /**
56
- * total amount of transactions
57
- * {Number}
58
- */
59
- #totalTransactions = 0
60
-
61
- #participants = []
62
- #participating = false
63
- #jail = []
64
-
65
- constructor() {
66
- super()
67
- return this.#init()
68
- }
69
-
70
- get nativeMints() {
71
- return this.#nativeMints
72
- }
73
-
74
- get nativeBurns() {
75
- return this.#nativeBurns
76
- }
77
-
78
- get nativeTransfers() {
79
- return this.#nativeTransfers
80
- }
81
-
82
- get totalTransactions() {
83
- return this.#totalTransactions
84
- }
85
-
86
- get nativeCalls() {
87
- return this.#nativeCalls
88
- }
89
-
90
- get totalSize() {
91
- return this.#totalSize
92
- }
93
-
94
- get lib() {
95
- return lib
96
- }
97
-
98
- get lastBlock() {
99
- return this.#lastBlock
100
- }
101
-
102
- get nativeToken() {
103
- return addresses.nativeToken
104
- }
105
-
106
- get validators() {
107
- return [...this.#validators]
108
- }
109
-
110
- get blocks() {
111
- return [...this.#blocks]
112
- }
113
-
114
- async hasTransactionToHandle() {
115
- const size = await transactionPoolStore.size()
116
- if (size > 0) return true
117
- return false
118
- }
119
-
120
- async #runEpoch() {
121
- this.#runningEpoch = true
122
- console.log('epoch');
123
- const validators = await this.staticCall(addresses.validators, 'validators')
124
- console.log({validators});
125
- if (!validators[peernet.selectedAccount]?.active) return
126
- const start = Date.now()
127
- try {
128
- await this.#createBlock()
129
- } catch (error) {
130
- console.error(error);
131
- }
132
-
133
- const end = Date.now()
134
- console.log(((end - start) / 1000) + ' s');
135
-
136
- if (await this.hasTransactionToHandle()) return this.#runEpoch()
137
- this.#runningEpoch = false
138
- // if (await this.hasTransactionToHandle() && !this.#runningEpoch) return this.#runEpoch()
139
- }
140
-
141
- async #setup() {
142
-
143
- const contracts = [{
144
- address: addresses.contractFactory,
145
- message: contractFactoryMessage
146
- }, {
147
- address: addresses.nativeToken,
148
- message: nativeTokenMessage
149
- }, {
150
- address: addresses.validators,
151
- message: validatorsMessage
152
- }, {
153
- address: addresses.nameService,
154
- message: nameServiceMessage
155
- }]
156
-
157
- await Promise.all(contracts.map(async ({address, message}) => {
158
- // console.log({message});
159
- message = await new ContractMessage(Uint8Array.from(message.split(',').map(string => Number(string))))
160
- await contractStore.put(address, message.encoded)
161
- }))
162
- console.log('handle native contracts');
163
- // handle native contracts
164
- }
165
-
166
- promiseRequests(promises) {
167
- return new Promise(async (resolve, reject) => {
168
- const timeout = setTimeout(() => {
169
- resolve([{index: 0, hash: '0x0'}])
170
- debug('sync timed out')
171
- }, 10_000)
172
-
173
- promises = await Promise.allSettled(promises);
174
- promises = promises.filter(({status}) => status === 'fulfilled')
175
- clearTimeout(timeout)
176
-
177
- promises = promises.map(({value}) => new peernet.protos['peernet-response'](value))
178
- promises = await Promise.all(promises)
179
- promises = promises.map(node => node.decoded.response)
180
- resolve(promises)
181
- })
182
-
183
- }
184
-
185
- getLatestBlock() {
186
- return this.#getLatestBlock()
187
- }
188
-
189
- async #getLatestBlock() {
190
- let promises = [];
191
-
192
- let data = await new peernet.protos['peernet-request']({request: 'lastBlock'});
193
- const node = await peernet.prepareMessage(data);
194
-
195
- for (const peer of peernet.connections) {
196
- if (peer.connected && peer.readyState === 'open' && peer.peerId !== this.id) {
197
- promises.push(peer.request(node.encoded));
198
- } else if (!peer.connected || peer.readyState !== 'open') {
199
- // todo: remove peer
200
- // reinitiate channel?
201
- }
202
- }
203
- promises = await this.promiseRequests(promises)
204
- let latest = {index: 0, hash: '0x0'}
205
-
206
- for (const value of promises) {
207
- if (value.index > latest.index) {
208
- latest.index = value.index;
209
- latest.hash = await value.hash();
210
- }
211
- }
212
-
213
- if (latest.hash && latest.hash !== '0x0') {
214
- latest = await peernet.get(latest.hash, block)
215
- latest = await new BlockMessage(latest)
216
- }
217
-
218
- return latest
219
- }
220
-
221
- async #init() {
222
- // this.node = await new Node()
223
- this.#participants = []
224
- this.#participating = false
225
- const initialized = await contractStore.has(addresses.contractFactory)
226
- if (!initialized) await this.#setup()
227
-
228
-
229
- this.utils = { BigNumber, formatUnits, parseUnits }
230
-
231
- this.state = new State()
232
-
233
- try {
234
- let localBlock
235
- try {
236
- localBlock = await chainStore.get('lastBlock')
237
- } catch{
238
- await chainStore.put('lastBlock', '0x0')
239
- localBlock = await chainStore.get('lastBlock')
240
- }
241
- localBlock = new TextDecoder().decode(localBlock)
242
- if (localBlock && localBlock !== '0x0') {
243
- localBlock = await peernet.get(localBlock, 'block')
244
- localBlock = await new BlockMessage(localBlock)
245
- this.#lastBlock = {...localBlock.decoded, hash: await localBlock.hash()}
246
- } else {
247
- const latestBlock = await this.#getLatestBlock()
248
- await this.#syncChain(latestBlock)
249
- }
250
- } catch (error) {
251
- console.log({e: error});
252
- }
253
-
254
- await peernet.addRequestHandler('bw-request-message', () => {
255
-
256
- return new BWMessage(peernet.client.bw) || { up: 0, down: 0 }
257
- })
258
-
259
- await peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this))
260
-
261
- peernet.subscribe('add-block', this.#addBlock.bind(this))
262
-
263
- peernet.subscribe('add-transaction', this.#addTransaction.bind(this))
264
-
265
- peernet.subscribe('validator:timeout', this.#validatorTimeout.bind(this))
266
-
267
- pubsub.subscribe('peer:connected', this.#peerConnected.bind(this))
268
-
269
-
270
- // load local blocks
271
- await this.resolveBlocks()
272
- this.#machine = await new Machine(this.#blocks)
273
- await this.#loadBlocks(this.#blocks)
274
- return this
275
- }
276
-
277
- async #validatorTimeout(validatorInfo) {
278
- setTimeout(() => {
279
- this.#jail.splice(this.jail.indexOf(validatorInfo.address), 1)
280
- }, validatorInfo.timeout)
281
- this.#jail.push(validatorInfo.address)
282
- }
283
-
284
- async #syncChain(lastBlock) {
285
- if (this.#chainSyncing || !lastBlock || !lastBlock.hash || !lastBlock.hash) return
286
-
287
- if (!this.lastBlock || Number(this.lastBlock.index) < Number(lastBlock.index)) {
288
- this.#chainSyncing = true
289
- // TODO: check if valid
290
- const localIndex = this.lastBlock ? this.lastBlock.index : 0
291
- const index = lastBlock.index
292
- await this.resolveBlock(lastBlock.hash)
293
- let blocksSynced = localIndex > 0 ? (localIndex > index ? localIndex - index : index - localIndex) : index
294
- debug(`synced ${blocksSynced} ${blocksSynced > 1 ? 'blocks' : 'block'}`)
295
-
296
- const end = this.#blocks.length
297
- const start = (this.#blocks.length - blocksSynced) - 1
298
- await this.#loadBlocks(this.blocks.slice(start))
299
- await this.#updateState(new BlockMessage(this.#blocks[this.#blocks.length - 1]))
300
- this.#chainSyncing = false
301
- }
302
- }
303
-
304
- async #peerConnected(peer) {
305
- let node = await new peernet.protos['peernet-request']({request: 'lastBlock'})
306
- node = await peernet.prepareMessage(node)
307
- let response = await peer.request(node.encoded)
308
- response = await new globalThis.peernet.protos['peernet-response'](response)
309
- let lastBlock = response.decoded.response
310
- this.#syncChain(lastBlock)
311
- }
312
-
313
- #epochTimeout
314
-
315
-
316
- async #lastBlockHandler() {
317
- return new peernet.protos['peernet-response']({response: { hash: this.#lastBlock?.hash, index: this.#lastBlock?.index }})
318
- }
319
-
320
- async resolveBlock(hash) {
321
- if (!hash) throw new Error(`expected hash, got: ${hash}`)
322
- let block = await peernet.get(hash, 'block')
323
- block = await new BlockMessage(block)
324
- if (!await peernet.has(hash, 'block')) await peernet.put(hash, block.encoded, 'block')
325
- const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength
326
- this.#totalSize += size
327
- block = {...block.decoded, hash}
328
- if (this.#blocks[block.index] && this.#blocks[block.index].hash !== block.hash) throw `invalid block ${hash} @${block.index}`
329
- this.#blocks[block.index] = block
330
- console.log(`resolved block: ${hash} @${block.index} ${formatBytes(size)}`);
331
- if (block.previousHash !== '0x0') {
332
- return this.resolveBlock(block.previousHash)
333
- }
334
- }
335
-
336
- async resolveBlocks() {
337
- try {
338
- const localBlock = await chainStore.get('lastBlock')
339
- const hash = new TextDecoder().decode(localBlock)
340
-
341
- if (hash && hash !== '0x0')
342
- await this.resolveBlock(hash)
343
- this.#lastBlock = this.#blocks[this.#blocks.length - 1]
344
-
345
- } catch {
346
- await chainStore.put('lastBlock', new TextEncoder().encode('0x0'))
347
- return this.resolveBlocks()
348
- // console.log(e);
349
- }
350
- }
351
-
352
- /**
353
- *
354
- * @param {Block[]} blocks
355
- */
356
- async #loadBlocks(blocks) {
357
- for (const block of blocks) {
358
- if (block && !block.loaded) {
359
- for (const transaction of block.transactions) {
360
- try {
361
- await this.#machine.execute(transaction.to, transaction.method, transaction.params)
362
- if (transaction.to === nativeToken) {
363
- this.#nativeCalls += 1
364
- if (transaction.method === 'burn') this.#nativeBurns += 1
365
- if (transaction.method === 'mint') this.#nativeMints += 1
366
- if (transaction.method === 'transfer') this.#nativeTransfers += 1
367
- }
368
- this.#totalTransactions += 1
369
- } catch (error) {
370
- console.log(error);
371
- }
372
- }
373
- this.#blocks[block.index].loaded = true
374
- debug(`loaded block: ${block.hash} @${block.index}`);
375
- }
376
- }
377
- }
378
-
379
- async #executeTransaction({hash, from, to, method, params, nonce}) {
380
- try {
381
- let result = await this.#machine.execute(to, method, params, from, nonce)
382
- // if (!result) result = this.#machine.state
383
- pubsub.publish(`transaction.completed.${hash}`, {status: 'fulfilled', hash})
384
- return result || 'no state change'
385
- } catch (error) {
386
- console.log(error);
387
- pubsub.publish(`transaction.completed.${hash}`, {status: 'fail', hash, error: error})
388
- throw error
389
- }
390
- }
391
-
392
- async #addBlock(block) {
393
- // console.log(block);
394
- const blockMessage = await new BlockMessage(block)
395
- await Promise.all(blockMessage.decoded.transactions
396
- .map(async transaction => transactionPoolStore.delete(transaction.hash)))
397
- const hash = await blockMessage.hash()
398
-
399
- await blockStore.put(hash, blockMessage.encoded)
400
-
401
- if (this.lastBlock.index < blockMessage.decoded.index) await this.#updateState(blockMessage)
402
- debug(`added block: ${hash}`)
403
- let promises = []
404
- let contracts = []
405
- for (let transaction of blockMessage.decoded.transactions) {
406
- // await transactionStore.put(transaction.hash, transaction.encoded)
407
- const index = contracts.indexOf(transaction.to)
408
- if (index === -1) contracts.push(transaction.to)
409
- // Todo: go trough all accounts
410
- promises.push(this.#executeTransaction(transaction))
411
-
412
- }
413
- try {
414
- promises = await Promise.allSettled(promises)
415
- for (let transaction of blockMessage.decoded.transactions) {
416
- pubsub.publish('transaction-processed', transaction)
417
- if (transaction.to === peernet.selectedAccount) pubsub.publish('account-transaction-processed', transaction)
418
- await accountsStore.put(transaction.from, String(transaction.nonce))
419
- }
420
-
421
- // todo finish state
422
- // for (const contract of contracts) {
423
- // const state = await this.#machine.get(contract, 'state')
424
- // // await stateStore.put(contract, state)
425
- // console.log(state);
426
- // }
427
-
428
-
429
- pubsub.publish('block-processed', blockMessage.decoded)
430
-
431
- } catch (error) {
432
- console.log({e: error});
433
- }
434
-
435
- }
436
-
437
- async #updateState(message) {
438
- const hash = await message.hash()
439
- this.#lastBlock = { hash, ...message.decoded }
440
- await this.state.updateState(message)
441
- await chainStore.put('lastBlock', hash)
442
- }
443
-
444
-
445
-
446
- async participate(address) {
447
- // TODO: validate participant
448
- // hold min amount of 50k ART for 7 days
449
- // lock the 50k
450
- // introduce peer-reputation
451
- // peerReputation(peerId)
452
- // {bandwith: {up, down}, uptime}
453
- this.#participating = true
454
- if (!await this.staticCall(addresses.validators, 'has', [address])) await this.createTransactionFrom(address, addresses.validators, 'addValidator', [address])
455
- if (await this.hasTransactionToHandle() && !this.#runningEpoch) await this.#runEpoch()
456
- }
457
-
458
- // todo filter tx that need to wait on prev nonce
459
- async #createBlock(limit = 1800) {
460
- // vote for transactions
461
- if (await transactionPoolStore.size() === 0) return;
462
-
463
- let transactions = await transactionPoolStore.values(this.transactionLimit)
464
- if (Object.keys(transactions)?.length === 0 ) return
465
-
466
- let block = {
467
- transactions: [],
468
- validators: [],
469
- fees: BigNumber.from(0)
470
- }
471
-
472
- // exclude failing tx
473
- transactions = await this.promiseTransactions(transactions)
474
- transactions = transactions.sort((a, b) => a.nonce - b.nonce)
475
- for (let transaction of transactions) {
476
- const hash = await transaction.hash()
477
- try {
478
- await this.#executeTransaction({...transaction.decoded, hash})
479
- block.transactions.push({hash, ...transaction.decoded})
480
- block.fees += Number(calculateFee(transaction.decoded))
481
- await accountsStore.put(transaction.decoded.from, new TextEncoder().encode(String(transaction.decoded.nonce)))
482
- } catch (e) {
483
- await transactionPoolStore.delete(hash)
484
- }
485
- }
486
- // don't add empty block
487
- if (block.transactions.length === 0) return
488
- const validators = await this.staticCall(addresses.validators, 'validators')
489
- console.log({validators});
490
- // block.validators = Object.keys(block.validators).reduce((set, key) => {
491
- // if (block.validators[key].active) {
492
- // push({
493
- // address: key
494
- // })
495
- // }
496
- // }, [])
497
- const peers = {}
498
- for (const entry of peernet.peerEntries) {
499
- peers[entry[0]] = entry[1]
500
- }
501
- for (const validator of Object.keys(validators)) {
502
- if (validators[validator].active) {
503
- const peer = peers[validator]
504
- if (peer && peer.connected) {
505
- let data = await new BWRequestMessage()
506
- const node = await peernet.prepareMessage(validator, data.encoded)
507
- try {
508
- const bw = await peer.request(node.encoded)
509
- console.log({bw});
510
- block.validators.push({
511
- address: validator,
512
- bw: bw.up + bw.down
513
- })
514
- } catch{}
515
-
516
- } else if (peernet.selectedAccount === validator) {
517
- block.validators.push({
518
- address: peernet.selectedAccount,
519
- bw: peernet.bw.up + peernet.bw.down
520
- })
521
-
522
- }
523
- }
524
-
525
- }
526
-
527
- console.log({validators: block.validators});
528
-
529
- block.reward = 150
530
- block.validators = block.validators.map(validator => {
531
- validator.reward = String(Number(block.fees) + block.reward / block.validators.length)
532
- delete validator.bw
533
- return validator
534
- })
535
- // block.validators = calculateValidatorReward(block.validators, block.fees)
536
-
537
- block.index = this.lastBlock?.index
538
- if (block.index === undefined) block.index = 0
539
- else block.index += 1
540
-
541
- block.previousHash = this.lastBlock?.hash || '0x0'
542
- block.timestamp = Date.now()
543
-
544
- const parts = String(block.fees).split('.')
545
- let decimals = 0
546
- if (parts[1]) {
547
- const potentional = parts[1].split('e')
548
- if (potentional[0] === parts[1]) {
549
- decimals = parts[1].length
550
- } else {
551
- parts[1] = potentional[0]
552
- decimals = Number(potentional[1]?.replace(/[+-]/g, '')) + Number(potentional[0].length)
553
- }
554
-
555
- }
556
- block.fees = Number.parseFloat(String(block.fees)).toFixed(decimals)
557
-
558
- try {
559
- await Promise.all(block.transactions
560
- .map(async transaction => transactionPoolStore.delete(transaction.hash)))
561
-
562
-
563
- let blockMessage = await new BlockMessage(block)
564
- const hash = await blockMessage.hash()
565
-
566
-
567
- await peernet.put(hash, blockMessage.encoded, 'block')
568
- await this.#updateState(blockMessage)
569
- debug(`created block: ${hash}`)
570
-
571
- peernet.publish('add-block', blockMessage.encoded)
572
- pubsub.publish('add-block', blockMessage.decoded)
573
- } catch (error) {
574
- throw new Error(`invalid block ${block}`)
575
- }
576
- // data = await this.#machine.execute(to, method, params)
577
- // transactionStore.put(message.hash, message.encoded)
578
- }
579
-
580
-
581
-
582
- async #addTransaction(transaction) {
583
- try {
584
- transaction = await new TransactionMessage(transaction)
585
- const hash = await transaction.hash()
586
- const has = await transactionPoolStore.has(hash)
587
- if (!has) await transactionPoolStore.put(hash, transaction.encoded)
588
- if (this.#participating && !this.#runningEpoch) this.#runEpoch()
589
- } catch (e) {
590
- console.log(e);
591
- throw new Error('invalid transaction')
592
- }
593
- }
594
- /**
595
- * whenever method = createContract params should hold the contract hash
596
- *
597
- * example: [hash]
598
- * createTransaction('0x0', 'createContract', [hash])
599
- *
600
- * @param {String} to - the contract address for the contract to interact with
601
- * @param {String} method - the method/function to run
602
- * @param {Array} params - array of paramters to apply to the contract method
603
- * @param {Number} nonce - total transaction count [optional]
604
- */
605
- async createTransaction(to, method, parameters, nonce, signature) {
606
- return this.createTransactionFrom(peernet.selectedAccount, to, method, parameters, nonce)
607
- }
608
- /**
609
- * every tx done is trough contracts so no need for amount
610
- * data is undefined when nothing is returned
611
- * error is thrown on error so undefined data doesn't mean there is an error...
612
- *
613
- * @param {Address} from - the sender address
614
- * @param {Address} to - the contract address for the contract to interact with
615
- * @param {String} method - the method/function to run
616
- * @param {Array} params - array of paramters to apply to the contract method
617
- * @param {Number} nonce - total transaction count [optional]
618
- */
619
- async createTransactionFrom(from, to, method, parameters, nonce) {
620
- const event = await super.createTransactionFrom(from, to, method, parameters, nonce)
621
- this.#addTransaction(event.message.encoded)
622
- return event
623
- }
624
-
625
- /**
626
- *
627
- * @param {Address} sender
628
- * @returns {globalMessage}
629
- */
630
- #createMessage(sender = peernet.selectedAccount) {
631
- return {
632
- sender,
633
- call: this.call,
634
- staticCall: this.staticCall,
635
- delegate: this.delegate,
636
- staticDelegate: this.staticDelegate
637
- }
638
- }
639
-
640
- /**
641
- *
642
- * @param {Address} sender
643
- * @param {Address} contract
644
- * @param {String} method
645
- * @param {Array} parameters
646
- * @returns
647
- */
648
- internalCall(sender, contract, method, parameters) {
649
- globalThis.msg = this.#createMessage(sender)
650
-
651
- return this.#machine.execute(contract, method, parameters)
652
- }
653
-
654
- /**
655
- *
656
- * @param {Address} contract
657
- * @param {String} method
658
- * @param {Array} parameters
659
- * @returns
660
- */
661
- call(contract, method, parameters) {
662
- globalThis.msg = this.#createMessage()
663
-
664
- return this.#machine.execute(contract, method, parameters)
665
- }
666
-
667
- staticCall(contract, method, parameters) {
668
- globalThis.msg = this.#createMessage()
669
- return this.#machine.get(contract, method, parameters)
670
- }
671
-
672
- delegate(contract, method, parameters) {
673
- globalThis.msg = this.#createMessage()
674
-
675
- return this.#machine.execute(contract, method, parameters)
676
- }
677
-
678
- staticDelegate(contract, method, parameters) {
679
- globalThis.msg = this.#createMessage()
680
-
681
- return this.#machine.get(contract, method, parameters)
682
- }
683
-
684
- mint(to, amount) {
685
- return this.call(addresses.nativeToken, 'mint', [to, amount])
686
- }
687
-
688
- transfer(from, to, amount) {
689
- return this.call(addresses.nativeToken, 'transfer', [from, to, amount])
690
- }
691
-
692
- get balances() {
693
- return this.staticCall(addresses.nativeToken, 'balances')
694
- }
695
-
696
- get contracts() {
697
- return this.staticCall(addresses.contractFactory, 'contracts')
698
- }
699
-
700
- deleteAll() {
701
- return this.#machine.deleteAll()
702
- }
703
-
704
- /**
705
- * lookup an address for a registered name using the builtin nameService
706
- * @check nameService
707
- *
708
- * @param {String} - contractName
709
- * @returns {String} - address
710
- *
711
- * @example chain.lookup('myCoolContractName') // qmqsfddfdgfg...
712
- */
713
- lookup(name) {
714
- return this.call(addresses.nameService, 'lookup', [name])
715
- }
716
- }
1
+ import { BigNumber, formatUnits, parseUnits, formatBytes } from '@leofcoin/utils'
2
+ import Machine from './machine.js'
3
+ import { ContractMessage, TransactionMessage, BlockMessage, BWMessage, BWRequestMessage } from '@leofcoin/messages'
4
+ import addresses, { nativeToken } from '@leofcoin/addresses'
5
+ import { contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage, calculateFee } from '@leofcoin/lib'
6
+ import State from './state.js'
7
+ import Contract from './contract.js'
8
+
9
+ globalThis.BigNumber = BigNumber
10
+
11
+ // check if browser or local
12
+ export default class Chain extends Contract {
13
+ /** {Address[]} */
14
+ #validators = []
15
+ /** {Block[]} */
16
+ #blocks = []
17
+
18
+ #machine
19
+ /** {Boolean} */
20
+ #runningEpoch = false
21
+
22
+ /** {Boolean} */
23
+ #chainSyncing = false
24
+
25
+ /** {Number} */
26
+ #totalSize = 0
27
+
28
+ /**
29
+ * {Block} {index, hash, previousHash}
30
+ */
31
+ #lastBlock = {index: 0, hash: '0x0', previousHash: '0x0'}
32
+
33
+ /**
34
+ * amount the native token has been iteracted with
35
+ */
36
+ #nativeCalls = 0
37
+
38
+ /**
39
+ * amount the native token has been iteracted with
40
+ */
41
+ #nativeTransfers = 0
42
+
43
+ /**
44
+ * amount of native token burned
45
+ * {Number}
46
+ */
47
+ #nativeBurns = 0
48
+
49
+ /**
50
+ * amount of native tokens minted
51
+ * {Number}
52
+ */
53
+ #nativeMints = 0
54
+
55
+ /**
56
+ * total amount of transactions
57
+ * {Number}
58
+ */
59
+ #totalTransactions = 0
60
+
61
+ #participants = []
62
+ #participating = false
63
+ #jail = []
64
+
65
+ constructor() {
66
+ super()
67
+ return this.#init()
68
+ }
69
+
70
+ get nativeMints() {
71
+ return this.#nativeMints
72
+ }
73
+
74
+ get nativeBurns() {
75
+ return this.#nativeBurns
76
+ }
77
+
78
+ get nativeTransfers() {
79
+ return this.#nativeTransfers
80
+ }
81
+
82
+ get totalTransactions() {
83
+ return this.#totalTransactions
84
+ }
85
+
86
+ get nativeCalls() {
87
+ return this.#nativeCalls
88
+ }
89
+
90
+ get totalSize() {
91
+ return this.#totalSize
92
+ }
93
+
94
+ get lib() {
95
+ return lib
96
+ }
97
+
98
+ get lastBlock() {
99
+ return this.#lastBlock
100
+ }
101
+
102
+ get nativeToken() {
103
+ return addresses.nativeToken
104
+ }
105
+
106
+ get validators() {
107
+ return [...this.#validators]
108
+ }
109
+
110
+ get blocks() {
111
+ return [...this.#blocks]
112
+ }
113
+
114
+ async hasTransactionToHandle() {
115
+ const size = await transactionPoolStore.size()
116
+ if (size > 0) return true
117
+ return false
118
+ }
119
+
120
+ async #runEpoch() {
121
+ this.#runningEpoch = true
122
+ console.log('epoch');
123
+ const validators = await this.staticCall(addresses.validators, 'validators')
124
+ console.log({validators});
125
+ if (!validators[peernet.selectedAccount]?.active) return
126
+ const start = Date.now()
127
+ try {
128
+ await this.#createBlock()
129
+ } catch (error) {
130
+ console.error(error);
131
+ }
132
+
133
+ const end = Date.now()
134
+ console.log(((end - start) / 1000) + ' s');
135
+
136
+ if (await this.hasTransactionToHandle()) return this.#runEpoch()
137
+ this.#runningEpoch = false
138
+ // if (await this.hasTransactionToHandle() && !this.#runningEpoch) return this.#runEpoch()
139
+ }
140
+
141
+ async #setup() {
142
+
143
+ const contracts = [{
144
+ address: addresses.contractFactory,
145
+ message: contractFactoryMessage
146
+ }, {
147
+ address: addresses.nativeToken,
148
+ message: nativeTokenMessage
149
+ }, {
150
+ address: addresses.validators,
151
+ message: validatorsMessage
152
+ }, {
153
+ address: addresses.nameService,
154
+ message: nameServiceMessage
155
+ }]
156
+
157
+ await Promise.all(contracts.map(async ({address, message}) => {
158
+ // console.log({message});
159
+ message = await new ContractMessage(Uint8Array.from(message.split(',').map(string => Number(string))))
160
+ await contractStore.put(address, message.encoded)
161
+ }))
162
+ console.log('handle native contracts');
163
+ // handle native contracts
164
+ }
165
+
166
+ promiseRequests(promises) {
167
+ return new Promise(async (resolve, reject) => {
168
+ const timeout = setTimeout(() => {
169
+ resolve([{index: 0, hash: '0x0'}])
170
+ debug('sync timed out')
171
+ }, 10_000)
172
+
173
+ promises = await Promise.allSettled(promises);
174
+ promises = promises.filter(({status}) => status === 'fulfilled')
175
+ clearTimeout(timeout)
176
+
177
+ promises = promises.map(({value}) => new peernet.protos['peernet-response'](value))
178
+ promises = await Promise.all(promises)
179
+ promises = promises.map(node => node.decoded.response)
180
+ resolve(promises)
181
+ })
182
+
183
+ }
184
+
185
+ getLatestBlock() {
186
+ return this.#getLatestBlock()
187
+ }
188
+
189
+ async #getLatestBlock() {
190
+ let promises = [];
191
+
192
+ let data = await new peernet.protos['peernet-request']({request: 'lastBlock'});
193
+ const node = await peernet.prepareMessage(data);
194
+
195
+ for (const peer of peernet.connections) {
196
+ if (peer.connected && peer.readyState === 'open' && peer.peerId !== this.id) {
197
+ promises.push(peer.request(node.encoded));
198
+ } else if (!peer.connected || peer.readyState !== 'open') {
199
+ // todo: remove peer
200
+ // reinitiate channel?
201
+ }
202
+ }
203
+ promises = await this.promiseRequests(promises)
204
+ let latest = {index: 0, hash: '0x0'}
205
+
206
+ for (const value of promises) {
207
+ if (value.index > latest.index) {
208
+ latest.index = value.index;
209
+ latest.hash = await value.hash();
210
+ }
211
+ }
212
+
213
+ if (latest.hash && latest.hash !== '0x0') {
214
+ latest = await peernet.get(latest.hash, block)
215
+ latest = await new BlockMessage(latest)
216
+ }
217
+
218
+ return latest
219
+ }
220
+
221
+ async #init() {
222
+ // this.node = await new Node()
223
+ this.#participants = []
224
+ this.#participating = false
225
+ const initialized = await contractStore.has(addresses.contractFactory)
226
+ if (!initialized) await this.#setup()
227
+
228
+
229
+ this.utils = { BigNumber, formatUnits, parseUnits }
230
+
231
+ this.state = new State()
232
+
233
+ try {
234
+ let localBlock
235
+ try {
236
+ localBlock = await chainStore.get('lastBlock')
237
+ } catch{
238
+ await chainStore.put('lastBlock', '0x0')
239
+ localBlock = await chainStore.get('lastBlock')
240
+ }
241
+ localBlock = new TextDecoder().decode(localBlock)
242
+ if (localBlock && localBlock !== '0x0') {
243
+ localBlock = await peernet.get(localBlock, 'block')
244
+ localBlock = await new BlockMessage(localBlock)
245
+ this.#lastBlock = {...localBlock.decoded, hash: await localBlock.hash()}
246
+ } else {
247
+ const latestBlock = await this.#getLatestBlock()
248
+ await this.#syncChain(latestBlock)
249
+ }
250
+ } catch (error) {
251
+ console.log({e: error});
252
+ }
253
+
254
+ await peernet.addRequestHandler('bw-request-message', () => {
255
+
256
+ return new BWMessage(peernet.client.bw) || { up: 0, down: 0 }
257
+ })
258
+
259
+ await peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this))
260
+
261
+ peernet.subscribe('add-block', this.#addBlock.bind(this))
262
+
263
+ peernet.subscribe('add-transaction', this.#addTransaction.bind(this))
264
+
265
+ peernet.subscribe('validator:timeout', this.#validatorTimeout.bind(this))
266
+
267
+ pubsub.subscribe('peer:connected', this.#peerConnected.bind(this))
268
+
269
+
270
+ // load local blocks
271
+ await this.resolveBlocks()
272
+ this.#machine = await new Machine(this.#blocks)
273
+ await this.#loadBlocks(this.#blocks)
274
+ return this
275
+ }
276
+
277
+ async #validatorTimeout(validatorInfo) {
278
+ setTimeout(() => {
279
+ this.#jail.splice(this.jail.indexOf(validatorInfo.address), 1)
280
+ }, validatorInfo.timeout)
281
+ this.#jail.push(validatorInfo.address)
282
+ }
283
+
284
+ async #syncChain(lastBlock) {
285
+ if (this.#chainSyncing || !lastBlock || !lastBlock.hash || !lastBlock.hash) return
286
+
287
+ if (!this.lastBlock || Number(this.lastBlock.index) < Number(lastBlock.index)) {
288
+ this.#chainSyncing = true
289
+ // TODO: check if valid
290
+ const localIndex = this.lastBlock ? this.lastBlock.index : 0
291
+ const index = lastBlock.index
292
+ await this.resolveBlock(lastBlock.hash)
293
+ let blocksSynced = localIndex > 0 ? (localIndex > index ? localIndex - index : index - localIndex) : index
294
+ debug(`synced ${blocksSynced} ${blocksSynced > 1 ? 'blocks' : 'block'}`)
295
+
296
+ const end = this.#blocks.length
297
+ const start = (this.#blocks.length - blocksSynced) - 1
298
+ await this.#loadBlocks(this.blocks.slice(start))
299
+ await this.#updateState(new BlockMessage(this.#blocks[this.#blocks.length - 1]))
300
+ this.#chainSyncing = false
301
+ }
302
+ }
303
+
304
+ async #peerConnected(peer) {
305
+ let node = await new peernet.protos['peernet-request']({request: 'lastBlock'})
306
+ node = await peernet.prepareMessage(node)
307
+ let response = await peer.request(node.encoded)
308
+ response = await new globalThis.peernet.protos['peernet-response'](response)
309
+ let lastBlock = response.decoded.response
310
+ this.#syncChain(lastBlock)
311
+ }
312
+
313
+ #epochTimeout
314
+
315
+
316
+ async #lastBlockHandler() {
317
+ return new peernet.protos['peernet-response']({response: { hash: this.#lastBlock?.hash, index: this.#lastBlock?.index }})
318
+ }
319
+
320
+ async resolveBlock(hash) {
321
+ if (!hash) throw new Error(`expected hash, got: ${hash}`)
322
+ let block = await peernet.get(hash, 'block')
323
+ block = await new BlockMessage(block)
324
+ if (!await peernet.has(hash, 'block')) await peernet.put(hash, block.encoded, 'block')
325
+ const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength
326
+ this.#totalSize += size
327
+ block = {...block.decoded, hash}
328
+ if (this.#blocks[block.index] && this.#blocks[block.index].hash !== block.hash) throw `invalid block ${hash} @${block.index}`
329
+ this.#blocks[block.index] = block
330
+ console.log(`resolved block: ${hash} @${block.index} ${formatBytes(size)}`);
331
+ if (block.previousHash !== '0x0') {
332
+ return this.resolveBlock(block.previousHash)
333
+ }
334
+ }
335
+
336
+ async resolveBlocks() {
337
+ try {
338
+ const localBlock = await chainStore.get('lastBlock')
339
+ const hash = new TextDecoder().decode(localBlock)
340
+
341
+ if (hash && hash !== '0x0')
342
+ await this.resolveBlock(hash)
343
+ this.#lastBlock = this.#blocks[this.#blocks.length - 1]
344
+
345
+ } catch {
346
+ await chainStore.put('lastBlock', new TextEncoder().encode('0x0'))
347
+ return this.resolveBlocks()
348
+ // console.log(e);
349
+ }
350
+ }
351
+
352
+ /**
353
+ *
354
+ * @param {Block[]} blocks
355
+ */
356
+ async #loadBlocks(blocks) {
357
+ for (const block of blocks) {
358
+ if (block && !block.loaded) {
359
+ for (const transaction of block.transactions) {
360
+ try {
361
+ await this.#machine.execute(transaction.to, transaction.method, transaction.params)
362
+ if (transaction.to === nativeToken) {
363
+ this.#nativeCalls += 1
364
+ if (transaction.method === 'burn') this.#nativeBurns += 1
365
+ if (transaction.method === 'mint') this.#nativeMints += 1
366
+ if (transaction.method === 'transfer') this.#nativeTransfers += 1
367
+ }
368
+ this.#totalTransactions += 1
369
+ } catch (error) {
370
+ console.log(error);
371
+ }
372
+ }
373
+ this.#blocks[block.index].loaded = true
374
+ debug(`loaded block: ${block.hash} @${block.index}`);
375
+ }
376
+ }
377
+ }
378
+
379
+ async #executeTransaction({hash, from, to, method, params, nonce}) {
380
+ try {
381
+ let result = await this.#machine.execute(to, method, params, from, nonce)
382
+ // if (!result) result = this.#machine.state
383
+ pubsub.publish(`transaction.completed.${hash}`, {status: 'fulfilled', hash})
384
+ return result || 'no state change'
385
+ } catch (error) {
386
+ console.log(error);
387
+ pubsub.publish(`transaction.completed.${hash}`, {status: 'fail', hash, error: error})
388
+ throw error
389
+ }
390
+ }
391
+
392
+ async #addBlock(block) {
393
+ // console.log(block);
394
+ const blockMessage = await new BlockMessage(block)
395
+ await Promise.all(blockMessage.decoded.transactions
396
+ .map(async transaction => transactionPoolStore.delete(transaction.hash)))
397
+ const hash = await blockMessage.hash()
398
+
399
+ await blockStore.put(hash, blockMessage.encoded)
400
+
401
+ if (this.lastBlock.index < blockMessage.decoded.index) await this.#updateState(blockMessage)
402
+ debug(`added block: ${hash}`)
403
+ let promises = []
404
+ let contracts = []
405
+ for (let transaction of blockMessage.decoded.transactions) {
406
+ // await transactionStore.put(transaction.hash, transaction.encoded)
407
+ const index = contracts.indexOf(transaction.to)
408
+ if (index === -1) contracts.push(transaction.to)
409
+ // Todo: go trough all accounts
410
+ promises.push(this.#executeTransaction(transaction))
411
+
412
+ }
413
+ try {
414
+ promises = await Promise.allSettled(promises)
415
+ for (let transaction of blockMessage.decoded.transactions) {
416
+ pubsub.publish('transaction-processed', transaction)
417
+ if (transaction.to === peernet.selectedAccount) pubsub.publish('account-transaction-processed', transaction)
418
+ await accountsStore.put(transaction.from, String(transaction.nonce))
419
+ }
420
+
421
+ // todo finish state
422
+ // for (const contract of contracts) {
423
+ // const state = await this.#machine.get(contract, 'state')
424
+ // // await stateStore.put(contract, state)
425
+ // console.log(state);
426
+ // }
427
+
428
+
429
+ pubsub.publish('block-processed', blockMessage.decoded)
430
+
431
+ } catch (error) {
432
+ console.log({e: error});
433
+ }
434
+
435
+ }
436
+
437
+ async #updateState(message) {
438
+ const hash = await message.hash()
439
+ this.#lastBlock = { hash, ...message.decoded }
440
+ await this.state.updateState(message)
441
+ await chainStore.put('lastBlock', hash)
442
+ }
443
+
444
+
445
+
446
+ async participate(address) {
447
+ // TODO: validate participant
448
+ // hold min amount of 50k ART for 7 days
449
+ // lock the 50k
450
+ // introduce peer-reputation
451
+ // peerReputation(peerId)
452
+ // {bandwith: {up, down}, uptime}
453
+ this.#participating = true
454
+ if (!await this.staticCall(addresses.validators, 'has', [address])) await this.createTransactionFrom(address, addresses.validators, 'addValidator', [address])
455
+ if (await this.hasTransactionToHandle() && !this.#runningEpoch) await this.#runEpoch()
456
+ }
457
+
458
+ // todo filter tx that need to wait on prev nonce
459
+ async #createBlock(limit = 1800) {
460
+ // vote for transactions
461
+ if (await transactionPoolStore.size() === 0) return;
462
+
463
+ let transactions = await transactionPoolStore.values(this.transactionLimit)
464
+ if (Object.keys(transactions)?.length === 0 ) return
465
+
466
+ let block = {
467
+ transactions: [],
468
+ validators: [],
469
+ fees: BigNumber.from(0)
470
+ }
471
+
472
+ // exclude failing tx
473
+ transactions = await this.promiseTransactions(transactions)
474
+ transactions = transactions.sort((a, b) => a.nonce - b.nonce)
475
+ for (let transaction of transactions) {
476
+ const hash = await transaction.hash()
477
+ try {
478
+ await this.#executeTransaction({...transaction.decoded, hash})
479
+ block.transactions.push({hash, ...transaction.decoded})
480
+ block.fees += Number(calculateFee(transaction.decoded))
481
+ await accountsStore.put(transaction.decoded.from, new TextEncoder().encode(String(transaction.decoded.nonce)))
482
+ } catch (e) {
483
+ await transactionPoolStore.delete(hash)
484
+ }
485
+ }
486
+ // don't add empty block
487
+ if (block.transactions.length === 0) return
488
+ const validators = await this.staticCall(addresses.validators, 'validators')
489
+ console.log({validators});
490
+ // block.validators = Object.keys(block.validators).reduce((set, key) => {
491
+ // if (block.validators[key].active) {
492
+ // push({
493
+ // address: key
494
+ // })
495
+ // }
496
+ // }, [])
497
+ const peers = {}
498
+ for (const entry of peernet.peerEntries) {
499
+ peers[entry[0]] = entry[1]
500
+ }
501
+ for (const validator of Object.keys(validators)) {
502
+ if (validators[validator].active) {
503
+ const peer = peers[validator]
504
+ if (peer && peer.connected) {
505
+ let data = await new BWRequestMessage()
506
+ const node = await peernet.prepareMessage(validator, data.encoded)
507
+ try {
508
+ const bw = await peer.request(node.encoded)
509
+ console.log({bw});
510
+ block.validators.push({
511
+ address: validator,
512
+ bw: bw.up + bw.down
513
+ })
514
+ } catch{}
515
+
516
+ } else if (peernet.selectedAccount === validator) {
517
+ block.validators.push({
518
+ address: peernet.selectedAccount,
519
+ bw: peernet.bw.up + peernet.bw.down
520
+ })
521
+
522
+ }
523
+ }
524
+
525
+ }
526
+
527
+ console.log({validators: block.validators});
528
+
529
+ block.reward = 150
530
+ block.validators = block.validators.map(validator => {
531
+ validator.reward = String(Number(block.fees) + block.reward / block.validators.length)
532
+ delete validator.bw
533
+ return validator
534
+ })
535
+ // block.validators = calculateValidatorReward(block.validators, block.fees)
536
+
537
+ block.index = this.lastBlock?.index
538
+ if (block.index === undefined) block.index = 0
539
+ else block.index += 1
540
+
541
+ block.previousHash = this.lastBlock?.hash || '0x0'
542
+ block.timestamp = Date.now()
543
+
544
+ const parts = String(block.fees).split('.')
545
+ let decimals = 0
546
+ if (parts[1]) {
547
+ const potentional = parts[1].split('e')
548
+ if (potentional[0] === parts[1]) {
549
+ decimals = parts[1].length
550
+ } else {
551
+ parts[1] = potentional[0]
552
+ decimals = Number(potentional[1]?.replace(/[+-]/g, '')) + Number(potentional[0].length)
553
+ }
554
+
555
+ }
556
+ block.fees = Number.parseFloat(String(block.fees)).toFixed(decimals)
557
+
558
+ try {
559
+ await Promise.all(block.transactions
560
+ .map(async transaction => transactionPoolStore.delete(transaction.hash)))
561
+
562
+
563
+ let blockMessage = await new BlockMessage(block)
564
+ const hash = await blockMessage.hash()
565
+
566
+
567
+ await peernet.put(hash, blockMessage.encoded, 'block')
568
+ await this.#updateState(blockMessage)
569
+ debug(`created block: ${hash}`)
570
+
571
+ peernet.publish('add-block', blockMessage.encoded)
572
+ pubsub.publish('add-block', blockMessage.decoded)
573
+ } catch (error) {
574
+ throw new Error(`invalid block ${block}`)
575
+ }
576
+ // data = await this.#machine.execute(to, method, params)
577
+ // transactionStore.put(message.hash, message.encoded)
578
+ }
579
+
580
+
581
+
582
+ async #addTransaction(transaction) {
583
+ try {
584
+ transaction = await new TransactionMessage(transaction)
585
+ const hash = await transaction.hash()
586
+ const has = await transactionPoolStore.has(hash)
587
+ if (!has) await transactionPoolStore.put(hash, transaction.encoded)
588
+ if (this.#participating && !this.#runningEpoch) this.#runEpoch()
589
+ } catch (e) {
590
+ console.log(e);
591
+ throw new Error('invalid transaction')
592
+ }
593
+ }
594
+ /**
595
+ * whenever method = createContract params should hold the contract hash
596
+ *
597
+ * example: [hash]
598
+ * createTransaction('0x0', 'createContract', [hash])
599
+ *
600
+ * @param {String} to - the contract address for the contract to interact with
601
+ * @param {String} method - the method/function to run
602
+ * @param {Array} params - array of paramters to apply to the contract method
603
+ * @param {Number} nonce - total transaction count [optional]
604
+ */
605
+ async createTransaction(to, method, parameters, nonce, signature) {
606
+ return this.createTransactionFrom(peernet.selectedAccount, to, method, parameters, nonce)
607
+ }
608
+ /**
609
+ * every tx done is trough contracts so no need for amount
610
+ * data is undefined when nothing is returned
611
+ * error is thrown on error so undefined data doesn't mean there is an error...
612
+ *
613
+ * @param {Address} from - the sender address
614
+ * @param {Address} to - the contract address for the contract to interact with
615
+ * @param {String} method - the method/function to run
616
+ * @param {Array} params - array of paramters to apply to the contract method
617
+ * @param {Number} nonce - total transaction count [optional]
618
+ */
619
+ async createTransactionFrom(from, to, method, parameters, nonce) {
620
+ const event = await super.createTransactionFrom(from, to, method, parameters, nonce)
621
+ this.#addTransaction(event.message.encoded)
622
+ return event
623
+ }
624
+
625
+ /**
626
+ *
627
+ * @param {Address} sender
628
+ * @returns {globalMessage}
629
+ */
630
+ #createMessage(sender = peernet.selectedAccount) {
631
+ return {
632
+ sender,
633
+ call: this.call,
634
+ staticCall: this.staticCall,
635
+ delegate: this.delegate,
636
+ staticDelegate: this.staticDelegate
637
+ }
638
+ }
639
+
640
+ /**
641
+ *
642
+ * @param {Address} sender
643
+ * @param {Address} contract
644
+ * @param {String} method
645
+ * @param {Array} parameters
646
+ * @returns
647
+ */
648
+ internalCall(sender, contract, method, parameters) {
649
+ globalThis.msg = this.#createMessage(sender)
650
+
651
+ return this.#machine.execute(contract, method, parameters)
652
+ }
653
+
654
+ /**
655
+ *
656
+ * @param {Address} contract
657
+ * @param {String} method
658
+ * @param {Array} parameters
659
+ * @returns
660
+ */
661
+ call(contract, method, parameters) {
662
+ globalThis.msg = this.#createMessage()
663
+
664
+ return this.#machine.execute(contract, method, parameters)
665
+ }
666
+
667
+ staticCall(contract, method, parameters) {
668
+ globalThis.msg = this.#createMessage()
669
+ return this.#machine.get(contract, method, parameters)
670
+ }
671
+
672
+ delegate(contract, method, parameters) {
673
+ globalThis.msg = this.#createMessage()
674
+
675
+ return this.#machine.execute(contract, method, parameters)
676
+ }
677
+
678
+ staticDelegate(contract, method, parameters) {
679
+ globalThis.msg = this.#createMessage()
680
+
681
+ return this.#machine.get(contract, method, parameters)
682
+ }
683
+
684
+ mint(to, amount) {
685
+ return this.call(addresses.nativeToken, 'mint', [to, amount])
686
+ }
687
+
688
+ transfer(from, to, amount) {
689
+ return this.call(addresses.nativeToken, 'transfer', [from, to, amount])
690
+ }
691
+
692
+ get balances() {
693
+ return this.staticCall(addresses.nativeToken, 'balances')
694
+ }
695
+
696
+ get contracts() {
697
+ return this.staticCall(addresses.contractFactory, 'contracts')
698
+ }
699
+
700
+ deleteAll() {
701
+ return this.#machine.deleteAll()
702
+ }
703
+
704
+ /**
705
+ * lookup an address for a registered name using the builtin nameService
706
+ * @check nameService
707
+ *
708
+ * @param {String} - contractName
709
+ * @returns {String} - address
710
+ *
711
+ * @example chain.lookup('myCoolContractName') // qmqsfddfdgfg...
712
+ */
713
+ lookup(name) {
714
+ return this.call(addresses.nameService, 'lookup', [name])
715
+ }
716
+ }