@leofcoin/chain 1.4.19 → 1.4.21

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,13 +1,14 @@
1
1
  {
2
2
  "name": "@leofcoin/chain",
3
- "version": "1.4.19",
3
+ "version": "1.4.21",
4
4
  "description": "Official javascript implementation",
5
5
  "main": "./dist/node.js",
6
6
  "module": "./dist/chain.esm",
7
7
  "exports": {
8
- "./node": "./src/node.js",
9
- "./chain": "./src/chain.js"
8
+ "./node": "./exports/node.js",
9
+ "./chain": "./exports/chain.js"
10
10
  },
11
+ "types": "./exports/types/chain.d.ts",
11
12
  "type": "module",
12
13
  "scripts": {
13
14
  "np": "np",
@@ -16,7 +17,7 @@
16
17
  "w": "rollup -c -w",
17
18
  "docs": "sh ./node_modules/.bin/esdoc src/chain.js",
18
19
  "workers": "cp ./../workers/src/** ./workers",
19
- "build": "npm run c && webpack && cp ./dist/*browser.js ./demo && cp -r ./dist/browser/workers/ ./demo",
20
+ "build": "npm run c",
20
21
  "demo": "jsproject --serve ./demo --port 5478 --open demo",
21
22
  "test": "node --openssl-legacy-provider test",
22
23
  "pack": "webpack"
@@ -30,6 +31,8 @@
30
31
  "author": "",
31
32
  "license": "MIT",
32
33
  "devDependencies": {
34
+ "@rollup/plugin-json": "^6.0.0",
35
+ "@rollup/plugin-typescript": "^11.0.0",
33
36
  "@types/bs58check": "^2.1.0",
34
37
  "@types/pako": "^2.0.0",
35
38
  "@types/randombytes": "^2.0.0",
@@ -38,6 +41,7 @@
38
41
  "eslint": "^8.28.0",
39
42
  "eslint-plugin-unicorn": "^45.0.0",
40
43
  "open": "^8.4.0",
44
+ "rollup": "^3.12.0",
41
45
  "tape": "^5.5.2",
42
46
  "tslib": "^2.4.1"
43
47
  },
@@ -48,7 +52,7 @@
48
52
  "@leofcoin/messages": "^1.2.0",
49
53
  "@leofcoin/multi-wallet": "^2.1.1",
50
54
  "@leofcoin/networks": "^1.0.0",
51
- "@leofcoin/peernet": "^0.18.0",
55
+ "@leofcoin/peernet": "^1.0.0",
52
56
  "@leofcoin/storage": "^3.0.6",
53
57
  "@leofcoin/utils": "^1.1.4",
54
58
  "@leofcoin/workers": "^1.3.5",
@@ -0,0 +1,15 @@
1
+ import json from '@rollup/plugin-json'
2
+ import typescript from '@rollup/plugin-typescript'
3
+ import tsConfig from './tsconfig.json' assert { type: 'json'}
4
+
5
+ export default [{
6
+ input: ['./src/chain.ts', './src/node.ts'],
7
+ output: {
8
+ dir: './exports',
9
+ format: 'es'
10
+ },
11
+ plugins: [
12
+ json(),
13
+ typescript(tsConfig)
14
+ ]
15
+ }]
@@ -2,9 +2,11 @@ import { BigNumber, formatUnits, parseUnits, formatBytes } from '@leofcoin/utils
2
2
  import Machine from './machine.js'
3
3
  import { ContractMessage, TransactionMessage, BlockMessage, BWMessage, BWRequestMessage } from '@leofcoin/messages'
4
4
  import addresses, { nativeToken } from '@leofcoin/addresses'
5
+ import { signTransaction } from '@leofcoin/lib'
5
6
  import { contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage, calculateFee } from '@leofcoin/lib'
6
7
  import State from './state.js'
7
8
  import Contract from './contract.js'
9
+ import { BigNumberish } from '@ethersproject/bignumber'
8
10
 
9
11
  globalThis.BigNumber = BigNumber
10
12
 
@@ -122,7 +124,7 @@ export default class Chain extends Contract {
122
124
  console.log('epoch');
123
125
  const validators = await this.staticCall(addresses.validators, 'validators')
124
126
  console.log({validators});
125
- if (!validators[peernet.selectedAccount]?.active) return
127
+ if (!validators[globalThis.peernet.selectedAccount]?.active) return
126
128
  const start = Date.now()
127
129
  try {
128
130
  await this.#createBlock()
@@ -296,7 +298,7 @@ export default class Chain extends Contract {
296
298
  const end = this.#blocks.length
297
299
  const start = (this.#blocks.length - blocksSynced) - 1
298
300
  await this.#loadBlocks(this.blocks.slice(start))
299
- await this.#updateState(this.#blocks[this.#blocks.length - 1])
301
+ await this.#updateState(new BlockMessage(this.#blocks[this.#blocks.length - 1]))
300
302
  this.#chainSyncing = false
301
303
  }
302
304
  }
@@ -451,7 +453,19 @@ async resolveBlock(hash) {
451
453
  // peerReputation(peerId)
452
454
  // {bandwith: {up, down}, uptime}
453
455
  this.#participating = true
454
- if (!await this.staticCall(addresses.validators, 'has', [address])) await this.createTransactionFrom(address, addresses.validators, 'addValidator', [address])
456
+ if (!await this.staticCall(addresses.validators, 'has', [address])) {
457
+ const rawTransaction = {
458
+ from: address,
459
+ to: addresses.validators,
460
+ method: 'addValidator',
461
+ params: [address],
462
+ nonce: (await this.getNonce(address)) + 1,
463
+ timestamp: Date.now()
464
+ }
465
+
466
+ const transaction = await signTransaction(rawTransaction, peernet.identity)
467
+ await this.sendTransaction(transaction)
468
+ }
455
469
  if (await this.hasTransactionToHandle() && !this.#runningEpoch) await this.#runEpoch()
456
470
  }
457
471
 
@@ -591,33 +605,13 @@ async resolveBlock(hash) {
591
605
  throw new Error('invalid transaction')
592
606
  }
593
607
  }
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
608
  /**
609
609
  * every tx done is trough contracts so no need for amount
610
610
  * data is undefined when nothing is returned
611
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)
612
+ **/
613
+ async sendTransaction(transaction) {
614
+ const event = await super.sendTransaction(transaction)
621
615
  this.#addTransaction(event.message.encoded)
622
616
  return event
623
617
  }
@@ -664,7 +658,7 @@ async resolveBlock(hash) {
664
658
  return this.#machine.execute(contract, method, parameters)
665
659
  }
666
660
 
667
- staticCall(contract, method, parameters) {
661
+ staticCall(contract, method, parameters?) {
668
662
  globalThis.msg = this.#createMessage()
669
663
  return this.#machine.get(contract, method, parameters)
670
664
  }
@@ -675,21 +669,21 @@ async resolveBlock(hash) {
675
669
  return this.#machine.execute(contract, method, parameters)
676
670
  }
677
671
 
678
- staticDelegate(contract, method, parameters) {
672
+ staticDelegate(contract: Address, method: string, parameters: []): any {
679
673
  globalThis.msg = this.#createMessage()
680
674
 
681
675
  return this.#machine.get(contract, method, parameters)
682
676
  }
683
677
 
684
- mint(to, amount) {
678
+ mint(to: string, amount: BigNumberish) {
685
679
  return this.call(addresses.nativeToken, 'mint', [to, amount])
686
680
  }
687
681
 
688
- transfer(from, to, amount) {
682
+ transfer(from: string, to: string, amount: BigNumberish) {
689
683
  return this.call(addresses.nativeToken, 'transfer', [from, to, amount])
690
684
  }
691
685
 
692
- get balances() {
686
+ get balances(): {address: BigNumberish} {
693
687
  return this.staticCall(addresses.nativeToken, 'balances')
694
688
  }
695
689
 
@@ -710,7 +704,7 @@ async resolveBlock(hash) {
710
704
  *
711
705
  * @example chain.lookup('myCoolContractName') // qmqsfddfdgfg...
712
706
  */
713
- lookup(name) {
707
+ lookup(name): {owner: string, address: string} {
714
708
  return this.call(addresses.nameService, 'lookup', [name])
715
709
  }
716
710
  }
@@ -13,7 +13,7 @@ export default class Node {
13
13
  networkName: 'leofcoin:peach',
14
14
  networkVersion: 'peach',
15
15
  stars: networks.leofcoin.peach.stars
16
- }) {
16
+ }): Promise<this> {
17
17
  globalThis.Peernet ? await new globalThis.Peernet(config) : await new Peernet(config)
18
18
  await nodeConfig(config)
19
19
 
@@ -0,0 +1,182 @@
1
+ import Protocol from "./protocol.js"
2
+ import { TransactionMessage, BlockMessage } from "@leofcoin/messages"
3
+ import { calculateFee } from '@leofcoin/lib'
4
+ import { formatBytes } from '@leofcoin/utils'
5
+
6
+ export default class Transaction extends Protocol {
7
+ constructor() {
8
+ super()
9
+ }
10
+
11
+ /**
12
+ *
13
+ * @param {Address[]} transactions
14
+ * @returns transactions to include
15
+ */
16
+ async getTransactions (transactions) {
17
+ return new Promise(async (resolve, reject) => {
18
+ let size = 0
19
+ const _transactions = []
20
+
21
+
22
+ const promises = await Promise.all(transactions
23
+ .map(async tx => {
24
+ tx = await new TransactionMessage(tx)
25
+ size += tx.encoded.length
26
+ if (!formatBytes(size).includes('MB') || formatBytes(size).includes('MB') && Number(formatBytes(size).split(' MB')[0]) <= 0.75) _transactions.push({...tx.decoded, hash: await tx.hash()})
27
+ else resolve(_transactions)
28
+ }))
29
+
30
+ return resolve(_transactions)
31
+ })
32
+
33
+ }
34
+
35
+ /**
36
+ *
37
+ * @param {Transaction[]} transactions An array containing Transactions
38
+ * @returns {TransactionMessage}
39
+ */
40
+ async promiseTransactions(transactions): Promise<TransactionMessage[]> {
41
+ transactions = await Promise.all(transactions.map(tx => new TransactionMessage(tx)))
42
+ return transactions
43
+ }
44
+
45
+ /**
46
+ *
47
+ * @param {Transaction[]} transactions An array containing Transactions
48
+ * @returns {Object} {transaction.decoded, transaction.hash}
49
+ */
50
+ async promiseTransactionsContent(transactions) {
51
+ transactions = await Promise.all(transactions.map(tx => new Promise(async (resolve, reject) => {
52
+ resolve({ ...tx.decoded, hash: await tx.hash() })
53
+ })))
54
+
55
+ return transactions
56
+ }
57
+
58
+ /**
59
+ * When a nonce isn't found for an address fallback to just checking the transactionnPoolStore
60
+ * @param {Address} address
61
+ * @returns {Number} nonce
62
+ */
63
+ async #getNonceFallback(address) {
64
+ let transactions = await globalThis.transactionPoolStore.values()
65
+ transactions = await this.promiseTransactions(transactions)
66
+ transactions = transactions.filter(tx => tx.decoded.from === address)
67
+ transactions = await this.promiseTransactionsContent(transactions)
68
+
69
+ if (this.lastBlock?.hash && transactions.length === 0 && this.lastBlock.hash !== '0x0') {
70
+
71
+ let block = await peernet.get(this.lastBlock.hash)
72
+ block = await new BlockMessage(block)
73
+
74
+ // for (let tx of block.decoded?.transactions) {
75
+ // tx = await peernet.get(tx, 'transaction')
76
+ // transactions.push(new TransactionMessage(tx))
77
+ // }
78
+ transactions = transactions.filter(tx => tx.from === address)
79
+ while (transactions.length === 0 && block.decoded.index !== 0 && block.decoded.previousHash !== '0x0') {
80
+ block = await globalThis.blockStore.get(block.decoded.previousHash)
81
+ block = await new BlockMessage(block)
82
+ transactions = block.decoded.transactions.filter(tx => tx.from === address)
83
+ }
84
+
85
+ }
86
+ if (transactions.length === 0) return 0
87
+
88
+ transactions = transactions.sort((a, b) => a.timestamp - b.timestamp)
89
+ return transactions[transactions.length - 1].nonce
90
+ }
91
+
92
+ /**
93
+ * Get amount of transactions by address
94
+ * @param {Address} address The address to get the nonce for
95
+ * @returns {Number} nonce
96
+ */
97
+ async getNonce(address) {
98
+ if (!await globalThis.accountsStore.has(address)) {
99
+ const nonce = await this.#getNonceFallback(address)
100
+ await globalThis.accountsStore.put(address, new TextEncoder().encode(String(nonce)))
101
+ }
102
+ // todo: are those in the pool in cluded also ? they need to be included!!!
103
+ let nonce = await globalThis.accountsStore.get(address)
104
+ nonce = new TextDecoder().decode(nonce)
105
+
106
+ let transactions = await globalThis.transactionPoolStore.values()
107
+ transactions = await this.promiseTransactions(transactions)
108
+ transactions = transactions.filter(tx => tx.decoded.from === address)
109
+ transactions = await this.promiseTransactionsContent(transactions)
110
+ for (const transaction of transactions) {
111
+ if (transaction.nonce > nonce) nonce = transaction.nonce
112
+ }
113
+ return Number(nonce)
114
+ }
115
+
116
+ async validateNonce(address, nonce) {
117
+ let previousNonce = await globalThis.accountsStore.get(address)
118
+ previousNonce = Number(new TextDecoder().decode(previousNonce))
119
+ if (previousNonce > nonce) throw new Error(`a transaction with a higher nonce already exists`)
120
+ if (previousNonce === nonce) throw new Error(`a transaction with the same nonce already exists`)
121
+
122
+ let transactions = await globalThis.transactionPoolStore.values()
123
+ transactions = await this.promiseTransactions(transactions)
124
+ transactions = transactions.filter(tx => tx.decoded.from === address)
125
+
126
+ for (const transaction of transactions) {
127
+ if (transaction.decoded.nonce > nonce) throw new Error(`a transaction with a higher nonce already exists`)
128
+ if (transaction.decoded.nonce === nonce) throw new Error(`a transaction with the same nonce already exists`)
129
+ }
130
+ }
131
+
132
+ isTransactionMessage(message) {
133
+ if (message instanceof TransactionMessage) return true
134
+ return false
135
+ }
136
+
137
+ async createTransaction(transaction) {
138
+ return {
139
+ from: transaction.from,
140
+ to: transaction.to,
141
+ method: transaction.method,
142
+ params: transaction.params,
143
+ timestamp: transaction.timestamp || Date.now(),
144
+ nonce: transaction.nonce || (await this.getNonce(transaction.from)) + 1
145
+ }
146
+ }
147
+
148
+ async sendTransaction(message) {
149
+ if (!this.isTransactionMessage(message)) message = new TransactionMessage(message)
150
+ if (!message.decoded.signature) throw new Error(`transaction not signed`)
151
+ if (message.decoded.nonce === undefined) throw new Error(`nonce required`)
152
+
153
+ try {
154
+ await this.validateNonce(message.decoded.from, message.decoded.nonce)
155
+ // todo check if signature is valid
156
+ const hash = await message.hash()
157
+ let data
158
+ const wait = new Promise(async (resolve, reject) => {
159
+ if (pubsub.subscribers[`transaction.completed.${hash}`]) {
160
+ const result = pubsub.subscribers[`transaction.completed.${hash}`].value
161
+ result.status === 'fulfilled' ? resolve(result.hash) : reject({hash: result.hash, error: result.error})
162
+ } else {
163
+ const completed = async result => {
164
+ result.status === 'fulfilled' ? resolve(result.hash) : reject({hash: result.hash, error: result.error})
165
+
166
+ setTimeout(async () => {
167
+ pubsub.unsubscribe(`transaction.completed.${hash}`, completed)
168
+ }, 10_000)
169
+ }
170
+ pubsub.subscribe(`transaction.completed.${hash}`, completed)
171
+ }
172
+ })
173
+ await globalThis.transactionPoolStore.put(hash, message.encoded)
174
+ debug(`Added ${hash} to the transaction pool`)
175
+ peernet.publish('add-transaction', message.encoded)
176
+ return {hash: hash, data, fee: await calculateFee(message.decoded), wait, message}
177
+ } catch (error) {
178
+ throw error
179
+ }
180
+
181
+ }
182
+ }
package/test/chain.js CHANGED
@@ -1,8 +1,8 @@
1
-
1
+ import { signTransaction } from '@leofcoin/lib'
2
2
 
3
3
  globalThis.DEBUG = true
4
- const Chain = await import('./../src/chain.js');
5
- const Node = await import('./../src/node.js');
4
+ const Chain = await import('./../exports/chain.js');
5
+ const Node = await import('./../exports/node.js');
6
6
  let imp = await import('../../networks/networks.js')
7
7
  const networks = imp.default
8
8
  console.log(networks.leofcoin.peach);
@@ -14,6 +14,7 @@
14
14
  })
15
15
  const chain = await new Chain.default()
16
16
  let start
17
+ // console.log(peernet.identity.sign());
17
18
  await chain.participate(peernet.selectedAccount)
18
19
  console.log(peernet.selectedAccount);
19
20
  const job = async () => {
@@ -22,7 +23,14 @@ const job = async () => {
22
23
  // // setTimeout(async () => {
23
24
  let tx
24
25
  // try {
25
- // tx = await chain.createTransaction(chain.nativeToken, 'grantRole', [peernet.selectedAccount, 'MINT'])
26
+ // const rawTransaction = await chain.createTransaction({
27
+ // from: peernet.selectedAccount,
28
+ // to: chain.nativeToken,
29
+ // method: 'grantRole',
30
+ // params: [peernet.selectedAccount, 'MINT']
31
+ // })
32
+ // const transaction = await signTransaction(rawTransaction, peernet.identity)
33
+ // tx = await chain.sendTransaction(transaction)
26
34
  // console.log({tx});
27
35
  // await tx.wait
28
36
 
@@ -30,14 +38,21 @@ const job = async () => {
30
38
  // console.log({e});
31
39
  // }
32
40
 
33
- // try {
34
- // tx = await chain.createTransaction(chain.nativeToken, 'mint', [peernet.selectedAccount, chain.utils.parseUnits('100000000000000').toString()])
35
-
41
+ // try {
42
+ // const rawTransaction = await chain.createTransaction({
43
+ // from: peernet.selectedAccount,
44
+ // to: chain.nativeToken,
45
+ // method: 'mint',
46
+ // params: [peernet.selectedAccount, chain.utils.parseUnits('100000000000000').toString()]
47
+ // })
48
+ // const transaction = await signTransaction(rawTransaction, peernet.identity)
49
+ // tx = await chain.sendTransaction(transaction)
50
+ // console.log({tx});
36
51
  // await tx.wait
37
52
  // } catch (e) {
38
53
  // console.log({e});
39
54
  // }
40
- return
55
+ // return
41
56
  console.log({nonce});
42
57
  let balances = await chain.balances
43
58
  console.log({balances});
@@ -46,11 +61,20 @@ const job = async () => {
46
61
  // console.log(`balance for ${Object.keys(balances)[1]}:${chain.utils.formatUnits(balances[Object.keys(balances)[1]]).toString()}`);
47
62
  // return
48
63
  let promises = []
49
- // nonce += 1
64
+ // nonce +=
65
+ nonce = await chain.getNonce(peernet.selectedAccount)
50
66
  for (let i = 0; i < 10; i++) {
51
67
  // contract , method, from, to, amount, (optional) nonce
52
68
  nonce += 1
53
- promises.push(chain.createTransaction(chain.nativeToken, 'transfer', [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()], nonce))
69
+ const rawTransaction = await chain.createTransaction({
70
+ from: peernet.selectedAccount,
71
+ to: chain.nativeToken,
72
+ method: 'transfer',
73
+ nonce,
74
+ params: [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()]
75
+ })
76
+ const transaction = await signTransaction(rawTransaction, peernet.identity)
77
+ promises.push(chain.sendTransaction(transaction))
54
78
  }
55
79
  promises = await Promise.allSettled(promises)
56
80
  promises = await Promise.allSettled(promises.map(({value}) => value.wait))
@@ -63,13 +87,20 @@ const job = async () => {
63
87
  // return
64
88
  // // setTimeout(async () => {
65
89
  promises = []
66
-
90
+ nonce = await chain.getNonce(peernet.selectedAccount)
67
91
  // nonce += 1
68
- for (let i = 0; i < 100; i++) {
92
+ for (let i = 0; i < 10; i++) {
69
93
  // contract , method, from, to, amount, (optional) nonce
70
-
71
94
  nonce += 1
72
- promises.push(chain.createTransaction(chain.nativeToken, 'transfer', [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()], nonce))
95
+ const rawTransaction = await chain.createTransaction({
96
+ from: peernet.selectedAccount,
97
+ to: chain.nativeToken,
98
+ method: 'transfer',
99
+ nonce,
100
+ params: [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()]
101
+ })
102
+ const transaction = await signTransaction(rawTransaction, peernet.identity)
103
+ promises.push(chain.sendTransaction(transaction))
73
104
  }
74
105
  promises = await Promise.allSettled(promises)
75
106
  promises = await Promise.allSettled(promises.map(({value}) => value.wait))
@@ -78,13 +109,23 @@ const job = async () => {
78
109
  console.log(`balance for ${Object.keys(balances)[1]}:${chain.utils.formatUnits(balances[Object.keys(balances)[1]]).toString()}`);
79
110
 
80
111
  promises = []
112
+ nonce = await chain.getNonce(peernet.selectedAccount)
81
113
 
114
+ const createSignSendTransaction = async (nonce) => {
115
+ const rawTransaction = await chain.createTransaction({
116
+ from: peernet.selectedAccount,
117
+ to: chain.nativeToken,
118
+ method: 'transfer',
119
+ nonce,
120
+ params: [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()]
121
+ })
122
+ const transaction = await signTransaction(rawTransaction, peernet.identity)
123
+ return chain.sendTransaction(transaction)
124
+ }
82
125
  // nonce += 1
83
126
  for (let i = 0; i < 1000; i++) {
84
- // contract , method, from, to, amount, (optional) nonce
85
-
86
127
  nonce += 1
87
- promises.push(chain.createTransaction(chain.nativeToken, 'transfer', [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()], nonce))
128
+ promises.push(createSignSendTransaction(nonce))
88
129
  }
89
130
  promises = await Promise.allSettled(promises)
90
131
  promises = await Promise.allSettled(promises.map(({value}) => value.wait))
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "es2022",
4
+ "target": "es2022",
5
+ "outDir": "./exports",
6
+ "moduleResolution":"NodeNext",
7
+ "declaration": true,
8
+ "declarationDir": "./exports/typings",
9
+ "allowJs": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "resolveJsonModule": true
12
+ },
13
+ "include": [
14
+ "./src/**/*"
15
+ ],
16
+ "exclude": ["./node_modules"]
17
+ }
@@ -1,234 +0,0 @@
1
- import Protocol from "./protocol.js"
2
- import MultiWallet from '@leofcoin/multi-wallet'
3
- import {CodecHash} from '@leofcoin/codec-format-interface'
4
- import bs32 from '@vandeurenglenn/base32'
5
- import { TransactionMessage, BlockMessage } from "@leofcoin/messages"
6
- import { calculateFee } from '@leofcoin/lib'
7
- import { formatBytes } from '@leofcoin/utils'
8
-
9
- export default class Transaction extends Protocol {
10
- constructor() {
11
- super()
12
- }
13
-
14
- /**
15
- *
16
- * @param {Address[]} transactions
17
- * @returns transactions to include
18
- */
19
- async getTransactions (transactions) {
20
- return new Promise(async (resolve, reject) => {
21
- let size = 0
22
- const _transactions = []
23
-
24
-
25
- const promises = await Promise.all(transactions
26
- .map(async tx => {
27
- tx = await new TransactionMessage(tx)
28
- size += tx.encoded.length
29
- if (!formatBytes(size).includes('MB') || formatBytes(size).includes('MB') && Number(formatBytes(size).split(' MB')[0]) <= 0.75) _transactions.push({...tx.decoded, hash: await tx.hash()})
30
- else resolve(_transactions)
31
- }))
32
-
33
- return resolve(_transactions)
34
- })
35
-
36
- }
37
-
38
- /**
39
- *
40
- * @param {Transaction[]} transactions An array containing Transactions
41
- * @returns {TransactionMessage}
42
- */
43
- async promiseTransactions(transactions) {
44
- transactions = await Promise.all(transactions.map(tx => new TransactionMessage(tx)))
45
- return transactions
46
- }
47
-
48
- /**
49
- *
50
- * @param {Transaction[]} transactions An array containing Transactions
51
- * @returns {Object} {transaction.decoded, transaction.hash}
52
- */
53
- async promiseTransactionsContent(transactions) {
54
- transactions = await Promise.all(transactions.map(tx => new Promise(async (resolve, reject) => {
55
- resolve({ ...tx.decoded, hash: await tx.hash() })
56
- })))
57
-
58
- return transactions
59
- }
60
-
61
- /**
62
- * When a nonce isn't found for an address fallback to just checking the transactionnPoolStore
63
- * @param {Address} address
64
- * @returns {Number} nonce
65
- */
66
- async #getNonceFallback(address) {
67
- let transactions = await transactionPoolStore.values(this.transactionLimit)
68
- transactions = await this.promiseTransactions(transactions)
69
- transactions = transactions.filter(tx => tx.decoded.from === address)
70
- transactions = await this.promiseTransactionsContent(transactions)
71
-
72
- if (this.lastBlock?.hash && transactions.length === 0 && this.lastBlock.hash !== '0x0') {
73
-
74
- let block = await peernet.get(this.lastBlock.hash)
75
- block = await new BlockMessage(block)
76
-
77
- // for (let tx of block.decoded?.transactions) {
78
- // tx = await peernet.get(tx, 'transaction')
79
- // transactions.push(new TransactionMessage(tx))
80
- // }
81
- transactions = transactions.filter(tx => tx.from === address)
82
- while (transactions.length === 0 && block.decoded.index !== 0 && block.decoded.previousHash !== '0x0') {
83
- block = await blockStore.get(block.decoded.previousHash)
84
- block = await new BlockMessage(block)
85
- transactions = block.decoded.transactions.filter(tx => tx.from === address)
86
- }
87
-
88
- }
89
- if (transactions.length === 0) return 0
90
-
91
- transactions = transactions.sort((a, b) => a.timestamp - b.timestamp)
92
- return transactions[transactions.length - 1].nonce
93
- }
94
-
95
- /**
96
- * Get amount of transactions by address
97
- * @param {Address} address The address to get the nonce for
98
- * @returns {Number} nonce
99
- */
100
- async getNonce(address) {
101
- if (!await accountsStore.has(address)) {
102
- const nonce = await this.#getNonceFallback(address)
103
- await accountsStore.put(address, new TextEncoder().encode(String(nonce)))
104
- }
105
- // todo: are those in the pool in cluded also ? they need to be included!!!
106
- let nonce = await accountsStore.get(address)
107
- nonce = new TextDecoder().decode(nonce)
108
- return Number(nonce)
109
- }
110
-
111
- /**
112
- * whenever method = createContract params should hold the contract hash
113
- *
114
- * example: [hash]
115
- * createTransaction('0x0', 'createContract', [hash])
116
- *
117
- * @param {String} to - the contract address for the contract to interact with
118
- * @param {String} method - the method/function to run
119
- * @param {Array} params - array of paramters to apply to the contract method
120
- * @param {Number} nonce - total transaction count [optional]
121
- */
122
- async createTransaction(to, method, parameters, nonce, signature) {
123
- return this.createTransactionFrom(peernet.selectedAccount, to, method, parameters, nonce)
124
- }
125
-
126
- /**
127
- *
128
- * @param {Transaction} transaction
129
- * @param {String} transaction.from address
130
- * @param {String} transaction.to address
131
- * @param {Object} transaction.params {}
132
- * @param {String} transaction.params.method get, call
133
- * @param {Buffer} transaction.params.data
134
- * @returns
135
- */
136
- async createTransactionHash(transaction) {
137
- // todo: validate
138
- const peernetHash = await new CodecHash(transaction, {name: 'transaction-message'})
139
- return peernetHash.digest
140
- }
141
-
142
- /**
143
- * @param {Transaction} transaction
144
- * @param {object} wallet any wallet/signer that supports sign(RAWtransaction)
145
- */
146
- async #signTransaction (transaction, wallet) {
147
- return wallet.sign(await this.createTransactionHash(transaction))
148
- }
149
-
150
- /**
151
- *
152
- * @param {RawTransaction} transaction
153
- * @param {Signer} signer
154
- * @returns {Transaction} a signed transaction
155
- */
156
- async signTransaction(transaction, signer) {
157
- let identity = await walletStore.get('identity')
158
- identity = JSON.parse(new TextDecoder().decode(identity))
159
- const wallet = new MultiWallet(peernet.network)
160
- await wallet.recover(identity.mnemonic)
161
- const account = await wallet.account(0).external(0)
162
- transaction.signature = await this.#signTransaction(transaction, account)
163
- return transaction
164
- }
165
-
166
- /**
167
- *
168
- * @param {Transaction} transaction
169
- * @param {Address} transaction.from
170
- * @param {Address} transaction.to
171
- * @param {String} transaction.method
172
- * @param {Array} transaction.params
173
- * @param {Number} transaction.nonce
174
- *
175
- * @returns {RawTransaction} transaction
176
- */
177
- async ensureNonce(transaction) {
178
- if (!transaction.from) transaction.from = peernet.selectedAccount
179
- transaction.timestamp = Date.now()
180
-
181
- if (transaction.nonce === undefined) {
182
- transaction.nonce = await this.getNonce(transaction.from)
183
- } else {
184
- let nonce = await accountsStore.get(transaction.from)
185
- nonce = new TextDecoder().decode(nonce)
186
- if (transaction.nonce < nonce) throw new Error(`a transaction with a higher nonce already exists`)
187
- if (transaction.nonce === nonce) throw new Error(`a transaction with the same nonce already exists`)
188
- }
189
- return transaction
190
- }
191
-
192
- /**
193
- * every tx done is trough contracts so no need for amount
194
- * data is undefined when nothing is returned
195
- * error is thrown on error so undefined data doesn't mean there is an error...
196
- *
197
- * @param {Address} from - the sender address
198
- * @param {Address} to - the contract address for the contract to interact with
199
- * @param {String} method - the method/function to run
200
- * @param {Array} params - array of paramters to apply to the contract method
201
- * @param {Number} nonce - total transaction count [optional]
202
- */
203
- async createTransactionFrom(from, to, method, parameters, nonce) {
204
- try {
205
- const rawTransaction = await this.ensureNonce({from, to, nonce, method, params: parameters})
206
- const transaction = await this.signTransaction(rawTransaction, from)
207
- const message = await new TransactionMessage(transaction)
208
- const hash = await message.hash()
209
-
210
- let data
211
- const wait = new Promise(async (resolve, reject) => {
212
- if (pubsub.subscribers[`transaction.completed.${hash}`]) {
213
- const result = pubsub.subscribers[`transaction.completed.${hash}`].value
214
- result.status === 'fulfilled' ? resolve(result.hash) : reject({hash: result.hash, error: result.error})
215
- } else {
216
- const completed = async result => {
217
- result.status === 'fulfilled' ? resolve(result.hash) : reject({hash: result.hash, error: result.error})
218
-
219
- setTimeout(async () => {
220
- pubsub.unsubscribe(`transaction.completed.${hash}`, completed)
221
- }, 10_000)
222
- }
223
- pubsub.subscribe(`transaction.completed.${hash}`, completed)
224
- }
225
- })
226
- await transactionPoolStore.put(hash, message.encoded)
227
- debug(`Added ${hash} to the transaction pool`)
228
- peernet.publish('add-transaction', message.encoded)
229
- return {hash: hash, data, fee: await calculateFee(message.decoded), wait, message}
230
- } catch (error) {
231
- throw error
232
- }
233
- }
234
- }
package/tsconfig.js DELETED
@@ -1,15 +0,0 @@
1
- export default {
2
-
3
- "esModuleInterop": true,
4
- 'allowSyntheticDefaultImports': true,
5
- "compilerOptions": {
6
- "outDir": "./dist",
7
- "esModuleInterop": true,
8
- 'allowSyntheticDefaultImports': true,
9
- "allowJs": true,
10
- "target": "es11"
11
- },
12
- "include": [
13
- "./src/**/*"
14
- ]
15
- }