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