@leofcoin/chain 1.4.20 → 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 +9 -5
- package/rollup.config.js +15 -0
- package/src/{chain.js → chain.ts} +25 -31
- package/src/{node.js → node.ts} +1 -1
- package/src/transaction.ts +182 -0
- package/test/chain.js +58 -17
- package/tsconfig.json +17 -0
- package/src/transaction.js +0 -234
- package/tsconfig.js +0 -15
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leofcoin/chain",
|
|
3
|
-
"version": "1.4.
|
|
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": "./
|
|
9
|
-
"./chain": "./
|
|
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
|
|
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.
|
|
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",
|
package/rollup.config.js
ADDED
|
@@ -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()
|
|
@@ -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]))
|
|
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
|
-
|
|
614
|
-
|
|
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
|
}
|
package/src/{node.js → node.ts}
RENAMED
|
@@ -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('./../
|
|
5
|
-
const Node = await import('./../
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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 +=
|
|
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
|
-
|
|
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 <
|
|
92
|
+
for (let i = 0; i < 10; i++) {
|
|
69
93
|
// contract , method, from, to, amount, (optional) nonce
|
|
70
|
-
|
|
71
94
|
nonce += 1
|
|
72
|
-
|
|
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(
|
|
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
|
+
}
|
package/src/transaction.js
DELETED
|
@@ -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
|
-
}
|