@leofcoin/chain 1.3.9 → 1.3.11

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.
@@ -0,0 +1,233 @@
1
+ import Protocol from "./protocol"
2
+ import MultiWallet from '@leofcoin/multi-wallet'
3
+ import {CodecHash} from '@leofcoin/codec-format-interface/dist/index'
4
+ import bs32 from '@vandeurenglenn/base32'
5
+ import { TransactionMessage } from "../../messages/src/messages"
6
+ import { calculateFee } from './../../lib/src/lib'
7
+ import { formatBytes } from '../../utils/src/utils.js'
8
+ export default class Transaction extends Protocol {
9
+ constructor() {
10
+ super()
11
+ }
12
+
13
+ /**
14
+ *
15
+ * @param {Address[]} transactions
16
+ * @returns transactions to include
17
+ */
18
+ async getTransactions (transactions) {
19
+ return new Promise(async (resolve, reject) => {
20
+ let size = 0
21
+ const _transactions = []
22
+
23
+
24
+ const promises = await Promise.all(transactions
25
+ .map(async tx => {
26
+ tx = await new TransactionMessage(tx)
27
+ size += tx.encoded.length
28
+ 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})
29
+ else resolve(_transactions)
30
+ }))
31
+
32
+ return resolve(_transactions)
33
+ })
34
+
35
+ }
36
+
37
+ /**
38
+ *
39
+ * @param {Transaction[]} transactions An array containing Transactions
40
+ * @returns {TransactionMessage}
41
+ */
42
+ async promiseTransactions(transactions) {
43
+ transactions = await Promise.all(transactions.map(tx => new TransactionMessage(tx)))
44
+ return transactions
45
+ }
46
+
47
+ /**
48
+ *
49
+ * @param {Transaction[]} transactions An array containing Transactions
50
+ * @returns {Object} {transaction.decoded, transaction.hash}
51
+ */
52
+ async promiseTransactionsContent(transactions) {
53
+ transactions = await Promise.all(transactions.map(tx => new Promise(async (resolve, reject) => {
54
+ resolve({ ...tx.decoded, hash: await tx.hash })
55
+ })))
56
+
57
+ return transactions
58
+ }
59
+
60
+ /**
61
+ * When a nonce isn't found for an address fallback to just checking the transactionnPoolStore
62
+ * @param {Address} address
63
+ * @returns {Number} nonce
64
+ */
65
+ async #getNonceFallback(address) {
66
+ let transactions = await transactionPoolStore.values()
67
+ transactions = await this.promiseTransactions(transactions)
68
+ transactions = transactions.filter(tx => tx.decoded.from === address)
69
+ transactions = await this.promiseTransactionsContent(transactions)
70
+
71
+ if (this.lastBlock?.hash && transactions.length === 0 && this.lastBlock.hash !== '0x0') {
72
+
73
+ let block = await peernet.get(this.lastBlock.hash)
74
+ block = await new BlockMessage(block)
75
+
76
+ // for (let tx of block.decoded?.transactions) {
77
+ // tx = await peernet.get(tx, 'transaction')
78
+ // transactions.push(new TransactionMessage(tx))
79
+ // }
80
+ transactions = transactions.filter(tx => tx.from === address)
81
+ while (transactions.length === 0 && block.decoded.index !== 0 && block.decoded.previousHash !== '0x0') {
82
+ block = await blockStore.get(block.decoded.previousHash)
83
+ block = await new BlockMessage(block)
84
+ transactions = block.decoded.transactions.filter(tx => tx.from === address)
85
+ }
86
+
87
+ }
88
+ if (transactions.length === 0) return 0
89
+
90
+ transactions = transactions.sort((a, b) => a.timestamp - b.timestamp)
91
+ return transactions[transactions.length - 1].nonce
92
+ }
93
+
94
+ /**
95
+ * Get amount of transactions by address
96
+ * @param {Address} address The address to get the nonce for
97
+ * @returns {Number} nonce
98
+ */
99
+ async getNonce(address) {
100
+ if (!await accountsStore.has(address)) {
101
+ const nonce = await this.#getNonceFallback(address)
102
+ await accountsStore.put(address, new TextEncoder().encode(String(nonce)))
103
+ }
104
+ // todo: are those in the pool in cluded also ? they need to be included!!!
105
+ let nonce = await accountsStore.get(address)
106
+ nonce = new TextDecoder().decode(nonce)
107
+ return Number(nonce)
108
+ }
109
+
110
+ /**
111
+ * whenever method = createContract params should hold the contract hash
112
+ *
113
+ * example: [hash]
114
+ * createTransaction('0x0', 'createContract', [hash])
115
+ *
116
+ * @param {String} to - the contract address for the contract to interact with
117
+ * @param {String} method - the method/function to run
118
+ * @param {Array} params - array of paramters to apply to the contract method
119
+ * @param {Number} nonce - total transaction count [optional]
120
+ */
121
+ async createTransaction(to, method, parameters, nonce, signature) {
122
+ return this.createTransactionFrom(peernet.selectedAccount, to, method, parameters, nonce)
123
+ }
124
+
125
+ /**
126
+ *
127
+ * @param {Transaction} transaction
128
+ * @param {String} transaction.from address
129
+ * @param {String} transaction.to address
130
+ * @param {Object} transaction.params {}
131
+ * @param {String} transaction.params.method get, call
132
+ * @param {Buffer} transaction.params.data
133
+ * @returns
134
+ */
135
+ async createTransactionHash(transaction) {
136
+ // todo: validate
137
+ const peernetHash = await new CodecHash(transaction, {name: 'transaction-message'})
138
+ return peernetHash.digest
139
+ }
140
+
141
+ /**
142
+ * @param {Transaction} transaction
143
+ * @param {object} wallet any wallet/signer that supports sign(RAWtransaction)
144
+ */
145
+ async #signTransaction (transaction, wallet) {
146
+ return wallet.sign(await this.createTransactionHash(transaction))
147
+ }
148
+
149
+ /**
150
+ *
151
+ * @param {RawTransaction} transaction
152
+ * @param {Signer} signer
153
+ * @returns {Transaction} a signed transaction
154
+ */
155
+ async signTransaction(transaction, signer) {
156
+ let identity = await walletStore.get('identity')
157
+ identity = JSON.parse(new TextDecoder().decode(identity))
158
+ const wallet = new MultiWallet(peernet.network)
159
+ wallet.recover(identity.mnemonic)
160
+ const account = wallet.account(0).external(0)
161
+ transaction.signature = await this.#signTransaction(transaction, account)
162
+ transaction.signature = bs32.encode(transaction.signature)
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
+
209
+ let data
210
+ const wait = () => new Promise(async (resolve, reject) => {
211
+ if (pubsub.subscribers[`transaction.completed.${await message.hash}`]) {
212
+ const result = pubsub.subscribers[`transaction.completed.${await message.hash}`].value
213
+ result.status === 'fulfilled' ? resolve(await result.hash) : reject({hash: await result.hash, error: result.error})
214
+ } else {
215
+ const completed = async result => {
216
+ result.status === 'fulfilled' ? resolve(await result.hash) : reject({hash: await result.hash, error: result.error})
217
+
218
+ setTimeout(async () => {
219
+ pubsub.unsubscribe(`transaction.completed.${await message.hash}`, completed)
220
+ }, 10_000)
221
+ }
222
+ pubsub.subscribe(`transaction.completed.${await message.hash}`, completed)
223
+ }
224
+ })
225
+ await transactionPoolStore.put(await message.hash, message.encoded)
226
+ peernet.publish('add-transaction', message.encoded)
227
+ return {hash: await message.hash, data, fee: await calculateFee(message.decoded), wait, message}
228
+ } catch (error) {
229
+ console.log(error)
230
+ throw error
231
+ }
232
+ }
233
+ }
@@ -0,0 +1,21 @@
1
+ type Address = string
2
+
3
+ interface Transaction {
4
+ to: Address,
5
+ from: Address,
6
+ method: String,
7
+ params: string[],
8
+ nonce: Number
9
+ }
10
+
11
+ interface RawTransaction extends Transaction {
12
+ timestamp: Number
13
+ }
14
+
15
+ interface globalMessage {
16
+ sender: Address,
17
+ call: Function,
18
+ staticCall: Function,
19
+ delegate: Function,
20
+ staticDelegate: Function
21
+ }
package/test/chain.js CHANGED
@@ -33,22 +33,22 @@ console.log(peernet.selectedAccount);
33
33
  const job = async () => {
34
34
  // setTimeout(async () => {
35
35
  let tx
36
- try {
37
- tx = await chain.createTransaction(chain.nativeToken, 'grantRole', [peernet.selectedAccount, 'MINT'])
38
- await tx.wait()
36
+ // try {
37
+ // tx = await chain.createTransaction(chain.nativeToken, 'grantRole', [peernet.selectedAccount, 'MINT'])
38
+ // await tx.wait()
39
39
 
40
- } catch (e) {
41
- console.log({e});
42
- }
40
+ // } catch (e) {
41
+ // console.log({e});
42
+ // }
43
43
 
44
- try {
45
- tx = await chain.createTransaction(chain.nativeToken, 'mint', [peernet.selectedAccount, chain.utils.parseUnits('100000000000000').toString()])
44
+ // try {
45
+ // tx = await chain.createTransaction(chain.nativeToken, 'mint', [peernet.selectedAccount, chain.utils.parseUnits('100000000000000').toString()])
46
46
 
47
- await tx.wait()
48
- } catch (e) {
49
- console.log({e});
50
- }
51
- return
47
+ // await tx.wait()
48
+ // } catch (e) {
49
+ // console.log({e});
50
+ // }
51
+ // return
52
52
  let nonce = await chain.getNonce(peernet.selectedAccount)
53
53
  console.log({nonce});
54
54
  // return
@@ -60,6 +60,7 @@ const job = async () => {
60
60
  promises.push(chain.createTransaction(chain.nativeToken, 'transfer', [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()], nonce))
61
61
  }
62
62
  promises = await Promise.allSettled(promises)
63
+ console.log(promises);
63
64
  promises = await Promise.allSettled(promises.map(({value}) => value.wait()))
64
65
  promises.forEach((item, i) => {
65
66
  if (item.reason) console.log(item.reason);
@@ -90,13 +91,14 @@ const job = async () => {
90
91
  promises = []
91
92
 
92
93
  // nonce += 1
93
- for (let i = 0; i < 100; i++) {
94
+ for (let i = 0; i < 1000; i++) {
94
95
  // contract , method, from, to, amount, (optional) nonce
95
96
 
96
97
  nonce += 1
97
98
  promises.push(chain.createTransaction(chain.nativeToken, 'transfer', [peernet.selectedAccount, '6zqut21djrRNJAniaTByovGhnBGs5h9wfkP35mzjZkEBZwnQVo', chain.utils.parseUnits('100').toString()], nonce))
98
99
  }
99
100
  promises = await Promise.allSettled(promises)
101
+ console.log(promises);
100
102
  promises = await Promise.allSettled(promises.map(({value}) => value.wait()))
101
103
  balances = await chain.balances
102
104
  console.log(`balance for ${Object.keys(balances)[0]}:${chain.utils.formatUnits(balances[Object.keys(balances)[0]]).toString()}`);
package/webpack.config.js CHANGED
@@ -7,10 +7,11 @@ module.exports = [{
7
7
  node: './dist/module/node.js'
8
8
  },
9
9
  plugins: [
10
+
10
11
  // Work around for Buffer is undefined:
11
12
  // https://github.com/webpack/changelog-v5/issues/10
12
13
  new webpack.ProvidePlugin({
13
- Buffer: ['buffer', 'Buffer'],
14
+ Buffer: ['buffer', 'Buffer'],
14
15
  }),
15
16
  new webpack.ProvidePlugin({
16
17
  process: 'process/browser',