@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.
- package/CHANGELOG.md +14 -14
- package/LICENSE +88 -88
- package/README.md +4 -4
- package/demo/index.html +25 -25
- package/examples/contracts/token.js +7 -7
- package/package.json +71 -71
- package/plugins/bundle.js +18 -18
- package/src/chain.js +716 -716
- package/src/config/config.js +14 -14
- package/src/config/main.js +4 -4
- package/src/config/protocol.js +5 -5
- package/src/contract.js +51 -51
- package/src/fee/config.js +3 -3
- package/src/machine.js +215 -215
- package/src/node.js +24 -24
- package/src/protocol.js +3 -3
- package/src/state.js +31 -31
- package/src/transaction.js +233 -233
- package/src/type.index.d.ts +20 -20
- package/src/typer.js +19 -19
- package/test/chain.js +120 -109
- package/test/contracts/token.js +40 -40
- package/test/create-genesis.js +66 -66
- package/tsconfig.js +14 -14
- package/workers/block-worker.js +40 -40
- package/workers/machine-worker.js +218 -218
- package/workers/pool-worker.js +28 -28
- package/workers/transaction-worker.js +19 -19
- package/workers/workers.js +8 -8
- package/block-worker.js +0 -1
- package/demo/865.browser.js +0 -10
- package/demo/chain.browser.js +0 -66842
- package/demo/generate-account.browser.js +0 -50
- package/demo/messages.browser.js +0 -328
- package/demo/multi-wallet.browser.js +0 -15
- package/demo/node.browser.js +0 -9858
- package/demo/pako.browser.js +0 -6900
- package/demo/peernet-swarm.browser.js +0 -839
- package/demo/storage.browser.js +0 -3724
- package/demo/workers/865.js +0 -10
- package/demo/workers/block-worker.js +0 -13175
- package/demo/workers/machine-worker.js +0 -13385
- package/demo/workers/pool-worker.js +0 -8503
- package/demo/workers/transaction-worker.js +0 -8495
- package/demo/wrtc.browser.js +0 -28
- package/dist/browser/workers/865.js +0 -10
- package/dist/browser/workers/block-worker.js +0 -9460
- package/dist/browser/workers/machine-worker.js +0 -9670
- package/dist/browser/workers/pool-worker.js +0 -4608
- package/dist/browser/workers/transaction-worker.js +0 -4600
- package/dist/chain.js +0 -10128
- package/dist/client-80bc8156.js +0 -491
- package/dist/commonjs-7fe3c381.js +0 -270
- package/dist/contracts/factory.js +0 -1
- package/dist/contracts/name-service.js +0 -1
- package/dist/contracts/native-token.js +0 -1
- package/dist/contracts/validators.js +0 -1
- package/dist/generate-account-445db122.js +0 -46
- package/dist/index-57f93805.js +0 -718
- package/dist/messages-bce1b91d-81af3b00.js +0 -315
- package/dist/module/chain.js +0 -10091
- package/dist/module/client-8031ec88.js +0 -489
- package/dist/module/commonjs-9005d5c0.js +0 -268
- package/dist/module/generate-account-489552b6.js +0 -44
- package/dist/module/index-ac2285c4.js +0 -688
- package/dist/module/messages-bce1b91d-eaf75d83.js +0 -302
- package/dist/module/node.js +0 -7049
- package/dist/module/workers/block-worker.js +0 -94
- package/dist/module/workers/machine-worker.js +0 -304
- package/dist/module/workers/pool-worker.js +0 -55
- package/dist/module/workers/transaction-worker.js +0 -47
- package/dist/node.js +0 -7061
- package/dist/standards/token.js +0 -1
- package/dist/workers/machine-worker.js +0 -1
- package/dist/workers/pool-worker.js +0 -1
- package/dist/workers/transaction-worker.js +0 -1
package/src/state.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import pako from 'pako'
|
|
2
|
-
|
|
3
|
-
export default class State {
|
|
4
|
-
constructor() {
|
|
5
|
-
// return this.#init()
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// async #init() {
|
|
9
|
-
// const state = await stateStore.get()
|
|
10
|
-
// for (const [key, value] of Object.entries(state)) {
|
|
11
|
-
//
|
|
12
|
-
// }
|
|
13
|
-
//
|
|
14
|
-
// return this
|
|
15
|
-
// }
|
|
16
|
-
|
|
17
|
-
async put(key, value, isCompressed = true) {
|
|
18
|
-
value = isCompressed ? value : await pako.deflate(value)
|
|
19
|
-
await stateStore.put(key, value)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async get(key, isCompressed = true) {
|
|
23
|
-
const value = await stateStore.get(key)
|
|
24
|
-
return isCompressed = true ? pako.inflate(value) : value
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
updateState(block) {
|
|
28
|
-
// block.decoded.index
|
|
29
|
-
// this.#isUpdateNeeded()
|
|
30
|
-
}
|
|
31
|
-
}
|
|
1
|
+
import pako from 'pako'
|
|
2
|
+
|
|
3
|
+
export default class State {
|
|
4
|
+
constructor() {
|
|
5
|
+
// return this.#init()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// async #init() {
|
|
9
|
+
// const state = await stateStore.get()
|
|
10
|
+
// for (const [key, value] of Object.entries(state)) {
|
|
11
|
+
//
|
|
12
|
+
// }
|
|
13
|
+
//
|
|
14
|
+
// return this
|
|
15
|
+
// }
|
|
16
|
+
|
|
17
|
+
async put(key, value, isCompressed = true) {
|
|
18
|
+
value = isCompressed ? value : await pako.deflate(value)
|
|
19
|
+
await stateStore.put(key, value)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async get(key, isCompressed = true) {
|
|
23
|
+
const value = await stateStore.get(key)
|
|
24
|
+
return isCompressed = true ? pako.inflate(value) : value
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
updateState(block) {
|
|
28
|
+
// block.decoded.index
|
|
29
|
+
// this.#isUpdateNeeded()
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/transaction.js
CHANGED
|
@@ -1,234 +1,234 @@
|
|
|
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
|
-
}
|
|
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
234
|
}
|
package/src/type.index.d.ts
CHANGED
|
@@ -1,21 +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
|
|
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
21
|
}
|
package/src/typer.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import {BigNumber} from '@leofcoin/utils'
|
|
2
|
-
|
|
3
|
-
const codec = '0x'
|
|
4
|
-
|
|
5
|
-
const isAddress = address => {
|
|
6
|
-
if (!address.startsWith(codec)) return false
|
|
7
|
-
if (address.length !== 64) return false
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export default {
|
|
11
|
-
isType: (type, value) => {
|
|
12
|
-
type = type.toLowercase()
|
|
13
|
-
if (type === 'string') return typeof(value) === type
|
|
14
|
-
if (type === 'number') return !Number.isNaN(Number(value))
|
|
15
|
-
if (type === 'address') return isAddress(value)
|
|
16
|
-
if (type === 'BigNumber') return BigNumber.isBigNumber(value)
|
|
17
|
-
},
|
|
18
|
-
isAddress
|
|
19
|
-
}
|
|
1
|
+
import {BigNumber} from '@leofcoin/utils'
|
|
2
|
+
|
|
3
|
+
const codec = '0x'
|
|
4
|
+
|
|
5
|
+
const isAddress = address => {
|
|
6
|
+
if (!address.startsWith(codec)) return false
|
|
7
|
+
if (address.length !== 64) return false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
isType: (type, value) => {
|
|
12
|
+
type = type.toLowercase()
|
|
13
|
+
if (type === 'string') return typeof(value) === type
|
|
14
|
+
if (type === 'number') return !Number.isNaN(Number(value))
|
|
15
|
+
if (type === 'address') return isAddress(value)
|
|
16
|
+
if (type === 'BigNumber') return BigNumber.isBigNumber(value)
|
|
17
|
+
},
|
|
18
|
+
isAddress
|
|
19
|
+
}
|