@leofcoin/chain 1.4.71 → 1.4.74
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/exports/browser/chain.js +1657 -7714
- package/exports/browser/{client-020c00d8-020c00d8.js → client-6072af1a-6072af1a.js} +3 -2
- package/exports/browser/{index-ed935219-188aa3ad.js → index-502d190c-60cfcc02.js} +2 -2
- package/exports/browser/{index-2d1bd076.js → index-fa2c6beb.js} +1 -1
- package/exports/browser/{messages-cc3774b5-653c2dbe.js → messages-cd01524c-8d88d84b.js} +2 -2
- package/exports/browser/{node-browser-1585d228.js → node-browser-b35d8f46.js} +4 -4
- package/exports/browser/node-browser.js +2 -2
- package/exports/chain.js +588 -605
- package/exports/typings/chain.d.ts +14 -42
- package/exports/typings/contract.d.ts +1 -0
- package/exports/typings/jobs/sync.d.ts +3 -0
- package/exports/typings/{state.d.ts → machine-state.d.ts} +2 -1
- package/exports/typings/protocol.d.ts +2 -0
- package/package.json +2 -1
package/exports/chain.js
CHANGED
|
@@ -1,242 +1,18 @@
|
|
|
1
1
|
import { formatBytes, BigNumber, formatUnits, parseUnits } from '@leofcoin/utils';
|
|
2
|
+
import { TransactionMessage, BlockMessage, ContractMessage, BWMessage, BWRequestMessage } from '@leofcoin/messages';
|
|
2
3
|
import addresses, { contractFactory, nativeToken, validators, nameService } from '@leofcoin/addresses';
|
|
4
|
+
import { calculateFee, createContractMessage, contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage, signTransaction } from '@leofcoin/lib';
|
|
5
|
+
import PQueue from 'p-queue';
|
|
3
6
|
import { randombytes } from '@leofcoin/crypto';
|
|
4
7
|
import EasyWorker from '@vandeurenglenn/easy-worker';
|
|
5
|
-
import { ContractMessage, TransactionMessage, BlockMessage, BWMessage, BWRequestMessage } from '@leofcoin/messages';
|
|
6
|
-
import { calculateFee, createContractMessage, contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage, signTransaction } from '@leofcoin/lib';
|
|
7
|
-
import pako from 'pako';
|
|
8
|
-
|
|
9
|
-
// import State from './state'
|
|
10
|
-
class Machine {
|
|
11
|
-
#contracts = {};
|
|
12
|
-
#nonces = {};
|
|
13
|
-
lastBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
14
|
-
constructor(blocks) {
|
|
15
|
-
return this.#init(blocks);
|
|
16
|
-
}
|
|
17
|
-
#createMessage(sender = peernet.selectedAccount) {
|
|
18
|
-
return {
|
|
19
|
-
sender,
|
|
20
|
-
call: this.execute,
|
|
21
|
-
staticCall: this.get.bind(this)
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
async #onmessage(data) {
|
|
25
|
-
switch (data.type) {
|
|
26
|
-
case 'contractError': {
|
|
27
|
-
console.warn(`removing contract ${await data.hash()}`);
|
|
28
|
-
await contractStore.delete(await data.hash());
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
case 'initError': {
|
|
32
|
-
console.error(`init error: ${data.message}`);
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
case 'executionError': {
|
|
36
|
-
// console.warn(`error executing transaction ${data.message}`);
|
|
37
|
-
pubsub.publish(data.id, { error: data.message });
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
case 'debug': {
|
|
41
|
-
for (const message of data.messages)
|
|
42
|
-
debug(message);
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
case 'machine-ready': {
|
|
46
|
-
this.lastBlock = data.lastBlock;
|
|
47
|
-
pubsub.publish('machine.ready', true);
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
case 'response': {
|
|
51
|
-
pubsub.publish(data.id, data.value || false);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async #init(blocks) {
|
|
57
|
-
return new Promise(async (resolve) => {
|
|
58
|
-
const machineReady = () => {
|
|
59
|
-
pubsub.unsubscribe('machine.ready', machineReady);
|
|
60
|
-
resolve(this);
|
|
61
|
-
};
|
|
62
|
-
pubsub.subscribe('machine.ready', machineReady);
|
|
63
|
-
this.worker = await new EasyWorker('node_modules/@leofcoin/workers/src/machine-worker.js', { serialization: 'advanced', type: 'module' });
|
|
64
|
-
this.worker.onmessage(this.#onmessage.bind(this));
|
|
65
|
-
// const blocks = await blockStore.values()
|
|
66
|
-
const contracts = await Promise.all([
|
|
67
|
-
contractStore.get(contractFactory),
|
|
68
|
-
contractStore.get(nativeToken),
|
|
69
|
-
contractStore.get(validators),
|
|
70
|
-
contractStore.get(nameService)
|
|
71
|
-
]);
|
|
72
|
-
const message = {
|
|
73
|
-
type: 'init',
|
|
74
|
-
input: {
|
|
75
|
-
contracts,
|
|
76
|
-
blocks,
|
|
77
|
-
peerid: peernet.peerId
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
this.worker.postMessage(message);
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
async #runContract(contractMessage) {
|
|
84
|
-
const hash = await contractMessage.hash();
|
|
85
|
-
return new Promise((resolve, reject) => {
|
|
86
|
-
const id = randombytes(20).toString('hex');
|
|
87
|
-
const onmessage = message => {
|
|
88
|
-
pubsub.unsubscribe(id, onmessage);
|
|
89
|
-
if (message?.error)
|
|
90
|
-
reject(message.error);
|
|
91
|
-
else
|
|
92
|
-
resolve(message);
|
|
93
|
-
};
|
|
94
|
-
pubsub.subscribe(id, onmessage);
|
|
95
|
-
this.worker.postMessage({
|
|
96
|
-
type: 'run',
|
|
97
|
-
id,
|
|
98
|
-
input: {
|
|
99
|
-
decoded: contractMessage.decoded,
|
|
100
|
-
encoded: contractMessage.encoded,
|
|
101
|
-
hash
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
108
|
-
* @param {Address} contract
|
|
109
|
-
* @param {String} method
|
|
110
|
-
* @param {Array} parameters
|
|
111
|
-
* @returns Promise<message>
|
|
112
|
-
*/
|
|
113
|
-
async execute(contract, method, parameters) {
|
|
114
|
-
try {
|
|
115
|
-
if (contract === contractFactory && method === 'registerContract') {
|
|
116
|
-
if (await this.has(parameters[0]))
|
|
117
|
-
throw new Error(`duplicate contract @${parameters[0]}`);
|
|
118
|
-
let message;
|
|
119
|
-
if (!await contractStore.has(parameters[0])) {
|
|
120
|
-
message = await peernet.get(parameters[0], 'contract');
|
|
121
|
-
message = await new ContractMessage(message);
|
|
122
|
-
await contractStore.put(await message.hash(), message.encoded);
|
|
123
|
-
}
|
|
124
|
-
if (!message) {
|
|
125
|
-
message = await contractStore.get(parameters[0]);
|
|
126
|
-
message = await new ContractMessage(message);
|
|
127
|
-
}
|
|
128
|
-
if (!await this.has(await message.hash()))
|
|
129
|
-
await this.#runContract(message);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
throw new Error(`contract deployment failed for ${parameters[0]}\n${error.message}`);
|
|
134
|
-
}
|
|
135
|
-
return new Promise((resolve, reject) => {
|
|
136
|
-
const id = randombytes(20).toString('hex');
|
|
137
|
-
const onmessage = message => {
|
|
138
|
-
pubsub.unsubscribe(id, onmessage);
|
|
139
|
-
if (message?.error)
|
|
140
|
-
reject(message.error);
|
|
141
|
-
else
|
|
142
|
-
resolve(message);
|
|
143
|
-
};
|
|
144
|
-
pubsub.subscribe(id, onmessage);
|
|
145
|
-
this.worker.postMessage({
|
|
146
|
-
type: 'execute',
|
|
147
|
-
id,
|
|
148
|
-
input: {
|
|
149
|
-
contract,
|
|
150
|
-
method,
|
|
151
|
-
params: parameters
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
get(contract, method, parameters) {
|
|
157
|
-
return new Promise((resolve, reject) => {
|
|
158
|
-
const id = randombytes(20).toString();
|
|
159
|
-
const onmessage = message => {
|
|
160
|
-
pubsub.unsubscribe(id, onmessage);
|
|
161
|
-
resolve(message);
|
|
162
|
-
};
|
|
163
|
-
pubsub.subscribe(id, onmessage);
|
|
164
|
-
this.worker.postMessage({
|
|
165
|
-
type: 'get',
|
|
166
|
-
id,
|
|
167
|
-
input: {
|
|
168
|
-
contract,
|
|
169
|
-
method,
|
|
170
|
-
params: parameters
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
async has(address) {
|
|
176
|
-
return new Promise((resolve, reject) => {
|
|
177
|
-
const id = randombytes(20).toString('hex');
|
|
178
|
-
const onmessage = message => {
|
|
179
|
-
pubsub.unsubscribe(id, onmessage);
|
|
180
|
-
if (message?.error)
|
|
181
|
-
reject(message.error);
|
|
182
|
-
else
|
|
183
|
-
resolve(message);
|
|
184
|
-
};
|
|
185
|
-
pubsub.subscribe(id, onmessage);
|
|
186
|
-
this.worker.postMessage({
|
|
187
|
-
type: 'has',
|
|
188
|
-
id,
|
|
189
|
-
input: {
|
|
190
|
-
address
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
async delete(hash) {
|
|
196
|
-
return contractStore.delete(hash);
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
*
|
|
200
|
-
* @returns Promise
|
|
201
|
-
*/
|
|
202
|
-
async deleteAll() {
|
|
203
|
-
let hashes = await contractStore.get();
|
|
204
|
-
hashes = Object.keys(hashes).map(hash => this.delete(hash));
|
|
205
|
-
return Promise.all(hashes);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
class State {
|
|
210
|
-
constructor() {
|
|
211
|
-
// return this.#init()
|
|
212
|
-
}
|
|
213
|
-
// async #init() {
|
|
214
|
-
// const state = await stateStore.get()
|
|
215
|
-
// for (const [key, value] of Object.entries(state)) {
|
|
216
|
-
//
|
|
217
|
-
// }
|
|
218
|
-
//
|
|
219
|
-
// return this
|
|
220
|
-
// }
|
|
221
|
-
async put(key, value, isCompressed = true) {
|
|
222
|
-
value = isCompressed ? value : await pako.deflate(value);
|
|
223
|
-
await stateStore.put(key, value);
|
|
224
|
-
}
|
|
225
|
-
async get(key, isCompressed = true) {
|
|
226
|
-
const value = await stateStore.get(key);
|
|
227
|
-
return isCompressed = pako.inflate(value) ;
|
|
228
|
-
}
|
|
229
|
-
updateState(block) {
|
|
230
|
-
// block.decoded.index
|
|
231
|
-
// this.#isUpdateNeeded()
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
8
|
|
|
235
9
|
const limit = 1800;
|
|
236
10
|
const transactionLimit = 1800;
|
|
237
11
|
const requestTimeout = 30_000;
|
|
238
12
|
const syncTimeout = 30_000;
|
|
239
13
|
class Protocol {
|
|
14
|
+
version;
|
|
15
|
+
resolveTimeout = 30_000;
|
|
240
16
|
get limit() {
|
|
241
17
|
return limit;
|
|
242
18
|
}
|
|
@@ -425,6 +201,8 @@ class Contract extends Transaction {
|
|
|
425
201
|
constructor() {
|
|
426
202
|
super();
|
|
427
203
|
}
|
|
204
|
+
async init() {
|
|
205
|
+
}
|
|
428
206
|
/**
|
|
429
207
|
*
|
|
430
208
|
* @param {Address} creator
|
|
@@ -466,98 +244,582 @@ class Contract extends Transaction {
|
|
|
466
244
|
}
|
|
467
245
|
}
|
|
468
246
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
#
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
#
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
#
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
247
|
+
// import State from './state'
|
|
248
|
+
class Machine {
|
|
249
|
+
#contracts = {};
|
|
250
|
+
#nonces = {};
|
|
251
|
+
lastBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
252
|
+
constructor(blocks) {
|
|
253
|
+
return this.#init(blocks);
|
|
254
|
+
}
|
|
255
|
+
#createMessage(sender = peernet.selectedAccount) {
|
|
256
|
+
return {
|
|
257
|
+
sender,
|
|
258
|
+
call: this.execute,
|
|
259
|
+
staticCall: this.get.bind(this)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
async #onmessage(data) {
|
|
263
|
+
switch (data.type) {
|
|
264
|
+
case 'contractError': {
|
|
265
|
+
console.warn(`removing contract ${await data.hash()}`);
|
|
266
|
+
await contractStore.delete(await data.hash());
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case 'initError': {
|
|
270
|
+
console.error(`init error: ${data.message}`);
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'executionError': {
|
|
274
|
+
// console.warn(`error executing transaction ${data.message}`);
|
|
275
|
+
pubsub.publish(data.id, { error: data.message });
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case 'debug': {
|
|
279
|
+
for (const message of data.messages)
|
|
280
|
+
debug(message);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
case 'machine-ready': {
|
|
284
|
+
this.lastBlock = data.lastBlock;
|
|
285
|
+
pubsub.publish('machine.ready', true);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
case 'response': {
|
|
289
|
+
pubsub.publish(data.id, data.value || false);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async #init(blocks) {
|
|
295
|
+
return new Promise(async (resolve) => {
|
|
296
|
+
const machineReady = () => {
|
|
297
|
+
pubsub.unsubscribe('machine.ready', machineReady);
|
|
298
|
+
resolve(this);
|
|
299
|
+
};
|
|
300
|
+
pubsub.subscribe('machine.ready', machineReady);
|
|
301
|
+
this.worker = await new EasyWorker('node_modules/@leofcoin/workers/src/machine-worker.js', { serialization: 'advanced', type: 'module' });
|
|
302
|
+
this.worker.onmessage(this.#onmessage.bind(this));
|
|
303
|
+
// const blocks = await blockStore.values()
|
|
304
|
+
const contracts = await Promise.all([
|
|
305
|
+
contractStore.get(contractFactory),
|
|
306
|
+
contractStore.get(nativeToken),
|
|
307
|
+
contractStore.get(validators),
|
|
308
|
+
contractStore.get(nameService)
|
|
309
|
+
]);
|
|
310
|
+
const message = {
|
|
311
|
+
type: 'init',
|
|
312
|
+
input: {
|
|
313
|
+
contracts,
|
|
314
|
+
blocks,
|
|
315
|
+
peerid: peernet.peerId
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
this.worker.postMessage(message);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async #runContract(contractMessage) {
|
|
322
|
+
const hash = await contractMessage.hash();
|
|
323
|
+
return new Promise((resolve, reject) => {
|
|
324
|
+
const id = randombytes(20).toString('hex');
|
|
325
|
+
const onmessage = message => {
|
|
326
|
+
pubsub.unsubscribe(id, onmessage);
|
|
327
|
+
if (message?.error)
|
|
328
|
+
reject(message.error);
|
|
329
|
+
else
|
|
330
|
+
resolve(message);
|
|
331
|
+
};
|
|
332
|
+
pubsub.subscribe(id, onmessage);
|
|
333
|
+
this.worker.postMessage({
|
|
334
|
+
type: 'run',
|
|
335
|
+
id,
|
|
336
|
+
input: {
|
|
337
|
+
decoded: contractMessage.decoded,
|
|
338
|
+
encoded: contractMessage.encoded,
|
|
339
|
+
hash
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
*
|
|
346
|
+
* @param {Address} contract
|
|
347
|
+
* @param {String} method
|
|
348
|
+
* @param {Array} parameters
|
|
349
|
+
* @returns Promise<message>
|
|
350
|
+
*/
|
|
351
|
+
async execute(contract, method, parameters) {
|
|
352
|
+
try {
|
|
353
|
+
if (contract === contractFactory && method === 'registerContract') {
|
|
354
|
+
if (await this.has(parameters[0]))
|
|
355
|
+
throw new Error(`duplicate contract @${parameters[0]}`);
|
|
356
|
+
let message;
|
|
357
|
+
if (!await contractStore.has(parameters[0])) {
|
|
358
|
+
message = await peernet.get(parameters[0], 'contract');
|
|
359
|
+
message = await new ContractMessage(message);
|
|
360
|
+
await contractStore.put(await message.hash(), message.encoded);
|
|
361
|
+
}
|
|
362
|
+
if (!message) {
|
|
363
|
+
message = await contractStore.get(parameters[0]);
|
|
364
|
+
message = await new ContractMessage(message);
|
|
365
|
+
}
|
|
366
|
+
if (!await this.has(await message.hash()))
|
|
367
|
+
await this.#runContract(message);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
throw new Error(`contract deployment failed for ${parameters[0]}\n${error.message}`);
|
|
372
|
+
}
|
|
373
|
+
return new Promise((resolve, reject) => {
|
|
374
|
+
const id = randombytes(20).toString('hex');
|
|
375
|
+
const onmessage = message => {
|
|
376
|
+
pubsub.unsubscribe(id, onmessage);
|
|
377
|
+
if (message?.error)
|
|
378
|
+
reject(message.error);
|
|
379
|
+
else
|
|
380
|
+
resolve(message);
|
|
381
|
+
};
|
|
382
|
+
pubsub.subscribe(id, onmessage);
|
|
383
|
+
this.worker.postMessage({
|
|
384
|
+
type: 'execute',
|
|
385
|
+
id,
|
|
386
|
+
input: {
|
|
387
|
+
contract,
|
|
388
|
+
method,
|
|
389
|
+
params: parameters
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
get(contract, method, parameters) {
|
|
395
|
+
return new Promise((resolve, reject) => {
|
|
396
|
+
const id = randombytes(20).toString();
|
|
397
|
+
const onmessage = message => {
|
|
398
|
+
pubsub.unsubscribe(id, onmessage);
|
|
399
|
+
resolve(message);
|
|
400
|
+
};
|
|
401
|
+
pubsub.subscribe(id, onmessage);
|
|
402
|
+
this.worker.postMessage({
|
|
403
|
+
type: 'get',
|
|
404
|
+
id,
|
|
405
|
+
input: {
|
|
406
|
+
contract,
|
|
407
|
+
method,
|
|
408
|
+
params: parameters
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
async has(address) {
|
|
414
|
+
return new Promise((resolve, reject) => {
|
|
415
|
+
const id = randombytes(20).toString('hex');
|
|
416
|
+
const onmessage = message => {
|
|
417
|
+
pubsub.unsubscribe(id, onmessage);
|
|
418
|
+
if (message?.error)
|
|
419
|
+
reject(message.error);
|
|
420
|
+
else
|
|
421
|
+
resolve(message);
|
|
422
|
+
};
|
|
423
|
+
pubsub.subscribe(id, onmessage);
|
|
424
|
+
this.worker.postMessage({
|
|
425
|
+
type: 'has',
|
|
426
|
+
id,
|
|
427
|
+
input: {
|
|
428
|
+
address
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
async delete(hash) {
|
|
434
|
+
return contractStore.delete(hash);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
*
|
|
438
|
+
* @returns Promise
|
|
439
|
+
*/
|
|
440
|
+
async deleteAll() {
|
|
441
|
+
let hashes = await contractStore.get();
|
|
442
|
+
hashes = Object.keys(hashes).map(hash => this.delete(hash));
|
|
443
|
+
return Promise.all(hashes);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const queue = new PQueue({ concurrency: 1, throwOnTimeout: true });
|
|
448
|
+
class State extends Contract {
|
|
449
|
+
#resolveErrored;
|
|
450
|
+
#lastResolvedTime;
|
|
451
|
+
#lastResolved;
|
|
452
|
+
#resolveErrorCount = 0;
|
|
453
|
+
#syncState;
|
|
454
|
+
#lastBlockInQue;
|
|
455
|
+
#syncErrorCount = 0;
|
|
456
|
+
#blockHashMap = new Map();
|
|
457
|
+
#chainSyncing = false;
|
|
458
|
+
#lastBlock = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
459
|
+
#blocks = [];
|
|
460
|
+
#knownBlocks = [];
|
|
461
|
+
#totalSize = 0;
|
|
462
|
+
#machine;
|
|
463
|
+
/**
|
|
464
|
+
* amount the native token has been iteracted with
|
|
465
|
+
*/
|
|
466
|
+
#nativeCalls = 0;
|
|
467
|
+
/**
|
|
468
|
+
* amount the native token has been iteracted with
|
|
469
|
+
*/
|
|
470
|
+
#nativeTransfers = 0;
|
|
471
|
+
/**
|
|
472
|
+
* amount of native token burned
|
|
473
|
+
* {Number}
|
|
474
|
+
*/
|
|
475
|
+
#nativeBurns = 0;
|
|
476
|
+
/**
|
|
477
|
+
* amount of native tokens minted
|
|
478
|
+
* {Number}
|
|
479
|
+
*/
|
|
480
|
+
#nativeMints = 0;
|
|
481
|
+
/**
|
|
482
|
+
* total amount of transactions
|
|
483
|
+
* {Number}
|
|
484
|
+
*/
|
|
485
|
+
#totalTransactions = 0;
|
|
486
|
+
get nativeMints() {
|
|
487
|
+
return this.#nativeMints;
|
|
488
|
+
}
|
|
489
|
+
get nativeBurns() {
|
|
490
|
+
return this.#nativeBurns;
|
|
491
|
+
}
|
|
492
|
+
get nativeTransfers() {
|
|
493
|
+
return this.#nativeTransfers;
|
|
494
|
+
}
|
|
495
|
+
get totalTransactions() {
|
|
496
|
+
return this.#totalTransactions;
|
|
497
|
+
}
|
|
498
|
+
get nativeCalls() {
|
|
499
|
+
return this.#nativeCalls;
|
|
500
|
+
}
|
|
501
|
+
get blocks() {
|
|
502
|
+
return [...this.#blocks];
|
|
503
|
+
}
|
|
504
|
+
get lastBlock() {
|
|
505
|
+
return this.#lastBlock;
|
|
506
|
+
}
|
|
507
|
+
get totalSize() {
|
|
508
|
+
return this.#totalSize;
|
|
509
|
+
}
|
|
510
|
+
get machine() {
|
|
511
|
+
return this.#machine;
|
|
512
|
+
}
|
|
513
|
+
constructor() {
|
|
514
|
+
super();
|
|
515
|
+
}
|
|
516
|
+
async init() {
|
|
517
|
+
if (super.init)
|
|
518
|
+
await super.init();
|
|
519
|
+
await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this));
|
|
520
|
+
await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler.bind(this));
|
|
521
|
+
try {
|
|
522
|
+
let localBlock;
|
|
523
|
+
try {
|
|
524
|
+
localBlock = await globalThis.chainStore.get('lastBlock');
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
await globalThis.chainStore.put('lastBlock', '0x0');
|
|
528
|
+
localBlock = await globalThis.chainStore.get('lastBlock');
|
|
529
|
+
}
|
|
530
|
+
localBlock = new TextDecoder().decode(localBlock);
|
|
531
|
+
if (localBlock && localBlock !== '0x0') {
|
|
532
|
+
localBlock = await globalThis.peernet.get(localBlock, 'block');
|
|
533
|
+
localBlock = await new BlockMessage(localBlock);
|
|
534
|
+
this.#lastBlock = { ...localBlock.decoded, hash: await localBlock.hash() };
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
if (globalThis.peernet?.connections.length > 0) {
|
|
538
|
+
const latestBlock = await this.#getLatestBlock();
|
|
539
|
+
await this.#syncChain(latestBlock);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
console.log({ e: error });
|
|
545
|
+
}
|
|
546
|
+
// load local blocks
|
|
547
|
+
await this.resolveBlocks();
|
|
548
|
+
this.#machine = await new Machine(this.#blocks);
|
|
549
|
+
await this.#loadBlocks(this.#blocks);
|
|
550
|
+
}
|
|
551
|
+
async updateState(message) {
|
|
552
|
+
const hash = await message.hash();
|
|
553
|
+
this.#lastBlock = { hash, ...message.decoded };
|
|
554
|
+
// await this.state.updateState(message)
|
|
555
|
+
await globalThis.chainStore.put('lastBlock', hash);
|
|
556
|
+
}
|
|
557
|
+
async #lastBlockHandler() {
|
|
558
|
+
return new globalThis.peernet.protos['peernet-response']({ response: { hash: this.#lastBlock?.hash, index: this.#lastBlock?.index } });
|
|
559
|
+
}
|
|
560
|
+
async #knownBlocksHandler() {
|
|
561
|
+
return new globalThis.peernet.protos['peernet-response']({ response: { blocks: this.#blocks.map((block) => block.hash) } });
|
|
562
|
+
}
|
|
563
|
+
getLatestBlock() {
|
|
564
|
+
return this.#getLatestBlock();
|
|
565
|
+
}
|
|
566
|
+
async getAndPutBlock(hash) {
|
|
567
|
+
// todo peernet resolves undefined blocks....
|
|
568
|
+
let block = await globalThis.peernet.get(hash, 'block');
|
|
569
|
+
if (block !== undefined) {
|
|
570
|
+
block = await new BlockMessage(block);
|
|
571
|
+
const { index } = block.decoded;
|
|
572
|
+
if (this.#blocks[index] && this.#blocks[index].hash !== block.hash)
|
|
573
|
+
throw `invalid block ${hash} @${index}`;
|
|
574
|
+
if (!await globalThis.peernet.has(hash, 'block'))
|
|
575
|
+
await globalThis.peernet.put(hash, block.encoded, 'block');
|
|
576
|
+
}
|
|
577
|
+
return block;
|
|
578
|
+
}
|
|
579
|
+
async #resolveBlock(hash) {
|
|
580
|
+
let index = this.#blockHashMap.get(hash);
|
|
581
|
+
if (this.#blocks[index]) {
|
|
582
|
+
if (this.#blocks[index].previousHash !== '0x0') {
|
|
583
|
+
return this.resolveBlock(this.#blocks[index].previousHash);
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const block = await this.getAndPutBlock(hash);
|
|
590
|
+
index = block.decoded.index;
|
|
591
|
+
const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
|
|
592
|
+
this.#totalSize += size;
|
|
593
|
+
this.#blocks[index] = { hash, ...block.decoded };
|
|
594
|
+
this.#blockHashMap.set(hash, index);
|
|
595
|
+
globalThis.debug(`resolved block: ${hash} @${index} ${formatBytes(size)}`);
|
|
596
|
+
this.#lastResolved = this.#blocks[index];
|
|
597
|
+
this.#lastResolvedTime = Date.now();
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
async resolveBlock(hash) {
|
|
601
|
+
if (!hash)
|
|
602
|
+
throw new Error(`expected hash, got: ${hash}`);
|
|
603
|
+
if (hash === '0x0')
|
|
604
|
+
return;
|
|
605
|
+
await queue.add(() => this.#resolveBlock(hash));
|
|
606
|
+
if (!this.#blockHashMap.has(this.#lastResolved.previousHash) && this.#lastResolved.previousHash !== '0x0')
|
|
607
|
+
return this.resolveBlock(this.#lastResolved.previousHash);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
async resolveBlocks() {
|
|
611
|
+
try {
|
|
612
|
+
const localBlock = await globalThis.chainStore.get('lastBlock');
|
|
613
|
+
const hash = new TextDecoder().decode(localBlock);
|
|
614
|
+
if (hash && hash !== '0x0')
|
|
615
|
+
await this.resolveBlock(hash);
|
|
616
|
+
this.#lastBlock = this.#blocks[this.#blocks.length - 1];
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
this.#resolveErrored = true;
|
|
620
|
+
this.#resolveErrorCount += 1;
|
|
621
|
+
// console.log(e);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async syncChain(lastBlock) {
|
|
625
|
+
if (!this.shouldSync) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
await queue.clear();
|
|
629
|
+
this.#chainSyncing = true;
|
|
630
|
+
if (!lastBlock)
|
|
631
|
+
lastBlock = await this.#getLatestBlock();
|
|
632
|
+
console.log('starting sync');
|
|
633
|
+
if (globalThis.peernet.connections.length === 0)
|
|
634
|
+
return 'connectionless';
|
|
635
|
+
try {
|
|
636
|
+
await this.#syncChain(lastBlock);
|
|
637
|
+
}
|
|
638
|
+
catch (error) {
|
|
639
|
+
this.#syncErrorCount += 1;
|
|
640
|
+
if (this.#syncErrorCount < 3)
|
|
641
|
+
await this.#syncChain(lastBlock);
|
|
642
|
+
this.#chainSyncing = false;
|
|
643
|
+
this.#syncErrorCount = 0;
|
|
644
|
+
return 'errored';
|
|
645
|
+
}
|
|
646
|
+
if (lastBlock.index === this.#lastBlockInQue?.index)
|
|
647
|
+
this.#lastBlockInQue = undefined;
|
|
648
|
+
this.#syncErrorCount = 0;
|
|
649
|
+
this.#chainSyncing = false;
|
|
650
|
+
if (this.#lastBlockInQue)
|
|
651
|
+
return this.syncChain(this.#lastBlockInQue);
|
|
652
|
+
return 'synced';
|
|
653
|
+
}
|
|
654
|
+
async #syncChain(lastBlock) {
|
|
655
|
+
try {
|
|
656
|
+
if (this.#knownBlocks?.length === Number(lastBlock.index) + 1) {
|
|
657
|
+
let promises = [];
|
|
658
|
+
promises = await Promise.allSettled(this.#knownBlocks.map(async (address) => {
|
|
659
|
+
const has = await globalThis.peernet.has(address, 'block');
|
|
660
|
+
return { has, address };
|
|
661
|
+
}));
|
|
662
|
+
promises = promises.filter(({ status, value }) => status === 'fulfilled' && !value.has);
|
|
663
|
+
await Promise.allSettled(promises.map(({ value }) => this.getAndPutBlock(value.address)));
|
|
664
|
+
}
|
|
665
|
+
if (!this.#lastBlock || Number(this.#lastBlock.index) < Number(lastBlock.index)) {
|
|
666
|
+
// TODO: check if valid
|
|
667
|
+
const localIndex = this.#lastBlock ? this.lastBlock.index : 0;
|
|
668
|
+
const index = lastBlock.index;
|
|
669
|
+
await this.resolveBlock(lastBlock.hash);
|
|
670
|
+
console.log('ok');
|
|
671
|
+
let blocksSynced = localIndex > 0 ? (localIndex > index ? localIndex - index : index - localIndex) : index;
|
|
672
|
+
globalThis.debug(`synced ${blocksSynced} ${blocksSynced > 1 ? 'blocks' : 'block'}`);
|
|
673
|
+
const start = (this.#blocks.length - blocksSynced) - 1;
|
|
674
|
+
if (this.#machine)
|
|
675
|
+
await this.#loadBlocks(this.blocks.slice(start));
|
|
676
|
+
await this.updateState(new BlockMessage(this.#blocks[this.#blocks.length - 1]));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
catch (error) {
|
|
680
|
+
console.log(error);
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
async #getLatestBlock() {
|
|
685
|
+
let promises = [];
|
|
686
|
+
let data = await new globalThis.peernet.protos['peernet-request']({ request: 'lastBlock' });
|
|
687
|
+
let node = await globalThis.peernet.prepareMessage(data);
|
|
688
|
+
for (const peer of globalThis.peernet?.connections) {
|
|
689
|
+
if (peer.connected && peer.version === this.version) {
|
|
690
|
+
promises.push(async () => {
|
|
691
|
+
try {
|
|
692
|
+
const result = await peer.request(node.encoded);
|
|
693
|
+
return { result, peer };
|
|
694
|
+
}
|
|
695
|
+
catch (error) {
|
|
696
|
+
throw error;
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
promises = await this.promiseRequests(promises);
|
|
702
|
+
let latest = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
703
|
+
promises = promises.sort((a, b) => b.index - a.index);
|
|
704
|
+
if (promises.length > 0)
|
|
705
|
+
latest = promises[0].value;
|
|
706
|
+
if (latest.hash && latest.hash !== '0x0') {
|
|
707
|
+
let message = await globalThis.peernet.get(latest.hash, 'block');
|
|
708
|
+
message = await new BlockMessage(message);
|
|
709
|
+
const hash = await message.hash();
|
|
710
|
+
if (hash !== latest.hash)
|
|
711
|
+
throw new Error('invalid block @getLatestBlock');
|
|
712
|
+
latest = { ...message.decoded, hash };
|
|
713
|
+
const peer = promises[0].peer;
|
|
714
|
+
if (peer.connected && peer.version === this.version) {
|
|
715
|
+
let data = await new globalThis.peernet.protos['peernet-request']({ request: 'knownBlocks' });
|
|
716
|
+
let node = await globalThis.peernet.prepareMessage(data);
|
|
717
|
+
let message = await peer.request(node);
|
|
718
|
+
message = await new globalThis.peernet.protos['peernet-response'](message);
|
|
719
|
+
this.#knownBlocks = message.decoded.response;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return latest;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
*
|
|
726
|
+
* @param {Block[]} blocks
|
|
727
|
+
*/
|
|
728
|
+
async #loadBlocks(blocks) {
|
|
729
|
+
let poolTransactionKeys = await globalThis.transactionPoolStore.keys();
|
|
730
|
+
for (const block of blocks) {
|
|
731
|
+
if (block && !block.loaded) {
|
|
732
|
+
for (const transaction of block.transactions) {
|
|
733
|
+
if (poolTransactionKeys.includes(transaction.hash))
|
|
734
|
+
await globalThis.transactionPoolStore.delete(transaction.hash);
|
|
735
|
+
try {
|
|
736
|
+
await this.#machine.execute(transaction.to, transaction.method, transaction.params);
|
|
737
|
+
if (transaction.to === nativeToken) {
|
|
738
|
+
this.#nativeCalls += 1;
|
|
739
|
+
if (transaction.method === 'burn')
|
|
740
|
+
this.#nativeBurns += 1;
|
|
741
|
+
if (transaction.method === 'mint')
|
|
742
|
+
this.#nativeMints += 1;
|
|
743
|
+
if (transaction.method === 'transfer')
|
|
744
|
+
this.#nativeTransfers += 1;
|
|
745
|
+
}
|
|
746
|
+
this.#totalTransactions += 1;
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
console.log(error);
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
this.#blocks[block.index].loaded = true;
|
|
754
|
+
globalThis.debug(`loaded block: ${block.hash} @${block.index}`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
promiseRequests(promises) {
|
|
760
|
+
return new Promise(async (resolve, reject) => {
|
|
761
|
+
const timeout = setTimeout(() => {
|
|
762
|
+
resolve([{ index: 0, hash: '0x0' }]);
|
|
763
|
+
globalThis.debug('sync timed out');
|
|
764
|
+
}, this.requestTimeout);
|
|
765
|
+
promises = await Promise.allSettled(promises);
|
|
766
|
+
promises = promises.filter(({ status }) => status === 'fulfilled');
|
|
767
|
+
clearTimeout(timeout);
|
|
768
|
+
if (promises.length > 0) {
|
|
769
|
+
promises = promises.map(async ({ value }) => {
|
|
770
|
+
const node = await new globalThis.peernet.protos['peernet-response'](value);
|
|
771
|
+
return { value: node.decoded.response, peer: value.peer };
|
|
772
|
+
});
|
|
773
|
+
promises = await Promise.all(promises);
|
|
774
|
+
resolve(promises);
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
resolve([]);
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
get canSync() {
|
|
782
|
+
if (this.#chainSyncing)
|
|
783
|
+
return false;
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
get shouldSync() {
|
|
787
|
+
if (this.canSync ||
|
|
788
|
+
this.#resolveErrored ||
|
|
789
|
+
!this.canSync && this.#lastResolvedTime + this.resolveTimeout > new Date().getTime())
|
|
790
|
+
return true;
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
793
|
+
async triggerSync() {
|
|
794
|
+
const latest = await this.#getLatestBlock();
|
|
795
|
+
return this.syncChain(latest);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
globalThis.BigNumber = BigNumber;
|
|
800
|
+
// check if browser or local
|
|
801
|
+
class Chain extends State {
|
|
802
|
+
#state;
|
|
803
|
+
#slotTime = 10000;
|
|
804
|
+
id;
|
|
805
|
+
utils;
|
|
806
|
+
/** {Address[]} */
|
|
807
|
+
#validators = [];
|
|
808
|
+
/** {Boolean} */
|
|
809
|
+
#runningEpoch = false;
|
|
810
|
+
#participants = [];
|
|
521
811
|
#participating = false;
|
|
522
812
|
#jail = [];
|
|
523
|
-
#knownBlocks = [];
|
|
524
813
|
constructor() {
|
|
525
814
|
super();
|
|
526
815
|
return this.#init();
|
|
527
816
|
}
|
|
528
|
-
get nativeMints() {
|
|
529
|
-
return this.#nativeMints;
|
|
530
|
-
}
|
|
531
|
-
get nativeBurns() {
|
|
532
|
-
return this.#nativeBurns;
|
|
533
|
-
}
|
|
534
|
-
get nativeTransfers() {
|
|
535
|
-
return this.#nativeTransfers;
|
|
536
|
-
}
|
|
537
|
-
get totalTransactions() {
|
|
538
|
-
return this.#totalTransactions;
|
|
539
|
-
}
|
|
540
|
-
get nativeCalls() {
|
|
541
|
-
return this.#nativeCalls;
|
|
542
|
-
}
|
|
543
|
-
get totalSize() {
|
|
544
|
-
return this.#totalSize;
|
|
545
|
-
}
|
|
546
|
-
get lib() {
|
|
547
|
-
return lib;
|
|
548
|
-
}
|
|
549
|
-
get lastBlock() {
|
|
550
|
-
return this.#lastBlock;
|
|
551
|
-
}
|
|
552
817
|
get nativeToken() {
|
|
553
818
|
return addresses.nativeToken;
|
|
554
819
|
}
|
|
555
820
|
get validators() {
|
|
556
821
|
return [...this.#validators];
|
|
557
822
|
}
|
|
558
|
-
get blocks() {
|
|
559
|
-
return [...this.#blocks];
|
|
560
|
-
}
|
|
561
823
|
async hasTransactionToHandle() {
|
|
562
824
|
const size = await globalThis.transactionPoolStore.size();
|
|
563
825
|
if (size > 0)
|
|
@@ -607,71 +869,6 @@ class Chain extends Contract {
|
|
|
607
869
|
console.log('handle native contracts');
|
|
608
870
|
// handle native contracts
|
|
609
871
|
}
|
|
610
|
-
promiseRequests(promises) {
|
|
611
|
-
return new Promise(async (resolve, reject) => {
|
|
612
|
-
const timeout = setTimeout(() => {
|
|
613
|
-
resolve([{ index: 0, hash: '0x0' }]);
|
|
614
|
-
globalThis.debug('sync timed out');
|
|
615
|
-
}, this.requestTimeout);
|
|
616
|
-
promises = await Promise.allSettled(promises);
|
|
617
|
-
promises = promises.filter(({ status }) => status === 'fulfilled');
|
|
618
|
-
clearTimeout(timeout);
|
|
619
|
-
if (promises.length > 0) {
|
|
620
|
-
promises = promises.map(async ({ value }) => {
|
|
621
|
-
const node = await new globalThis.peernet.protos['peernet-response'](value.result);
|
|
622
|
-
return { value: node.decoded.response, peer: value.peer };
|
|
623
|
-
});
|
|
624
|
-
promises = await Promise.all(promises);
|
|
625
|
-
resolve(promises);
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
resolve([]);
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
getLatestBlock() {
|
|
633
|
-
return this.#getLatestBlock();
|
|
634
|
-
}
|
|
635
|
-
async #getLatestBlock() {
|
|
636
|
-
let promises = [];
|
|
637
|
-
let data = await new globalThis.peernet.protos['peernet-request']({ request: 'lastBlock' });
|
|
638
|
-
let node = await globalThis.peernet.prepareMessage(data);
|
|
639
|
-
for (const peer of globalThis.peernet?.connections) {
|
|
640
|
-
if (peer.connected && peer.version === this.version) {
|
|
641
|
-
promises.push(async () => {
|
|
642
|
-
try {
|
|
643
|
-
const result = await peer.request(node.encoded);
|
|
644
|
-
return { result, peer };
|
|
645
|
-
}
|
|
646
|
-
catch (error) {
|
|
647
|
-
throw error;
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
promises = await this.promiseRequests(promises);
|
|
653
|
-
let latest = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
654
|
-
promises = promises.sort((a, b) => b.index - a.index);
|
|
655
|
-
if (promises.length > 0)
|
|
656
|
-
latest = promises[0].value;
|
|
657
|
-
if (latest.hash && latest.hash !== '0x0') {
|
|
658
|
-
let message = await globalThis.peernet.get(latest.hash, 'block');
|
|
659
|
-
message = await new BlockMessage(message);
|
|
660
|
-
const hash = await message.hash();
|
|
661
|
-
if (hash !== latest.hash)
|
|
662
|
-
throw new Error('invalid block @getLatestBlock');
|
|
663
|
-
latest = { ...message.decoded, hash };
|
|
664
|
-
const peer = promises[0].peer;
|
|
665
|
-
if (peer.connected && peer.version === this.version) {
|
|
666
|
-
let data = await new globalThis.peernet.protos['peernet-request']({ request: 'knownBlocks' });
|
|
667
|
-
let node = await globalThis.peernet.prepareMessage(data);
|
|
668
|
-
let message = await peer.request(node);
|
|
669
|
-
message = await new globalThis.peernet.protos['peernet-response'](message);
|
|
670
|
-
this.#knownBlocks = message.decoded.response;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
return latest;
|
|
674
|
-
}
|
|
675
872
|
async #clearPool() {
|
|
676
873
|
await globalThis.transactionPoolStore.clear();
|
|
677
874
|
}
|
|
@@ -703,7 +900,7 @@ class Chain extends Contract {
|
|
|
703
900
|
if (!initialized)
|
|
704
901
|
await this.#setup();
|
|
705
902
|
this.utils = { BigNumber, formatUnits, parseUnits };
|
|
706
|
-
this.#state = new State()
|
|
903
|
+
// this.#state = new State()
|
|
707
904
|
await globalThis.peernet.addRequestHandler('bw-request-message', () => {
|
|
708
905
|
return new BWMessage(globalThis.peernet.client.bw) || { up: 0, down: 0 };
|
|
709
906
|
});
|
|
@@ -713,43 +910,13 @@ class Chain extends Contract {
|
|
|
713
910
|
// })
|
|
714
911
|
await globalThis.peernet.addRequestHandler('transactionPool', this.#transactionPoolHandler.bind(this));
|
|
715
912
|
await globalThis.peernet.addRequestHandler('version', this.#versionHandler.bind(this));
|
|
716
|
-
await globalThis.peernet.addRequestHandler('lastBlock', this.#lastBlockHandler.bind(this));
|
|
717
|
-
await globalThis.peernet.addRequestHandler('knownBlocks', this.#knownBlocksHandler.bind(this));
|
|
718
913
|
globalThis.peernet.subscribe('add-block', this.#addBlock.bind(this));
|
|
719
914
|
globalThis.peernet.subscribe('invalid-transaction', this.#invalidTransaction.bind(this));
|
|
720
915
|
globalThis.peernet.subscribe('send-transaction', this.#sendTransaction.bind(this));
|
|
721
916
|
globalThis.peernet.subscribe('validator:timeout', this.#validatorTimeout.bind(this));
|
|
722
917
|
globalThis.pubsub.subscribe('peer:connected', this.#peerConnected.bind(this));
|
|
723
918
|
// todo some functions rely on state
|
|
724
|
-
|
|
725
|
-
let localBlock;
|
|
726
|
-
try {
|
|
727
|
-
localBlock = await globalThis.chainStore.get('lastBlock');
|
|
728
|
-
}
|
|
729
|
-
catch {
|
|
730
|
-
await globalThis.chainStore.put('lastBlock', '0x0');
|
|
731
|
-
localBlock = await globalThis.chainStore.get('lastBlock');
|
|
732
|
-
}
|
|
733
|
-
localBlock = new TextDecoder().decode(localBlock);
|
|
734
|
-
if (localBlock && localBlock !== '0x0') {
|
|
735
|
-
localBlock = await globalThis.peernet.get(localBlock, 'block');
|
|
736
|
-
localBlock = await new BlockMessage(localBlock);
|
|
737
|
-
this.#lastBlock = { ...localBlock.decoded, hash: await localBlock.hash() };
|
|
738
|
-
}
|
|
739
|
-
else {
|
|
740
|
-
if (globalThis.peernet?.connections.length > 0) {
|
|
741
|
-
const latestBlock = await this.#getLatestBlock();
|
|
742
|
-
await this.#syncChain(latestBlock);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
catch (error) {
|
|
747
|
-
console.log({ e: error });
|
|
748
|
-
}
|
|
749
|
-
// load local blocks
|
|
750
|
-
await this.resolveBlocks();
|
|
751
|
-
this.#machine = await new Machine(this.#blocks);
|
|
752
|
-
await this.#loadBlocks(this.#blocks);
|
|
919
|
+
await super.init();
|
|
753
920
|
globalThis.globalThis.pubsub.publish('chain:ready', true);
|
|
754
921
|
return this;
|
|
755
922
|
}
|
|
@@ -763,82 +930,6 @@ class Chain extends Contract {
|
|
|
763
930
|
}, validatorInfo.timeout);
|
|
764
931
|
this.#jail.push(validatorInfo.address);
|
|
765
932
|
}
|
|
766
|
-
async triggerSync() {
|
|
767
|
-
const latest = await this.#getLatestBlock();
|
|
768
|
-
return this.syncChain(latest);
|
|
769
|
-
}
|
|
770
|
-
async syncChain(lastBlock) {
|
|
771
|
-
if (!lastBlock)
|
|
772
|
-
lastBlock = await this.#getLatestBlock();
|
|
773
|
-
if (this.#chainSyncing) {
|
|
774
|
-
console.log('already syncing');
|
|
775
|
-
if (!this.#lastBlockInQue || lastBlock.index > this.#lastBlockInQue.index)
|
|
776
|
-
this.#lastBlockInQue = lastBlock;
|
|
777
|
-
return 'syncing';
|
|
778
|
-
}
|
|
779
|
-
console.log('starting sync');
|
|
780
|
-
this.#chainSyncing = true;
|
|
781
|
-
const syncPromise = (lastBlock) => new Promise(async (resolve, reject) => {
|
|
782
|
-
const timeout = setTimeout(() => {
|
|
783
|
-
this.#chainSyncing = false;
|
|
784
|
-
this.#syncErrorCount = 0;
|
|
785
|
-
reject('timedOut');
|
|
786
|
-
}, 60000);
|
|
787
|
-
try {
|
|
788
|
-
await this.#syncChain(lastBlock);
|
|
789
|
-
clearTimeout(timeout);
|
|
790
|
-
resolve(true);
|
|
791
|
-
}
|
|
792
|
-
catch (error) {
|
|
793
|
-
clearTimeout(timeout);
|
|
794
|
-
reject(error);
|
|
795
|
-
}
|
|
796
|
-
});
|
|
797
|
-
if (globalThis.peernet.connections.length === 0)
|
|
798
|
-
return 'connectionless';
|
|
799
|
-
try {
|
|
800
|
-
await syncPromise(lastBlock);
|
|
801
|
-
}
|
|
802
|
-
catch (error) {
|
|
803
|
-
console.log(error);
|
|
804
|
-
this.#syncErrorCount += 1;
|
|
805
|
-
if (this.#syncErrorCount < 3)
|
|
806
|
-
await syncPromise(lastBlock);
|
|
807
|
-
this.#chainSyncing = false;
|
|
808
|
-
this.#syncErrorCount = 0;
|
|
809
|
-
return 'errored';
|
|
810
|
-
}
|
|
811
|
-
if (lastBlock.index === this.#lastBlockInQue?.index)
|
|
812
|
-
this.#lastBlockInQue = undefined;
|
|
813
|
-
this.#syncErrorCount = 0;
|
|
814
|
-
this.#chainSyncing = false;
|
|
815
|
-
if (this.#lastBlockInQue)
|
|
816
|
-
return this.syncChain(this.#lastBlockInQue);
|
|
817
|
-
return 'synced';
|
|
818
|
-
}
|
|
819
|
-
async #syncChain(lastBlock) {
|
|
820
|
-
if (this.#knownBlocks?.length === Number(lastBlock.index) + 1) {
|
|
821
|
-
let promises = [];
|
|
822
|
-
promises = await Promise.allSettled(this.#knownBlocks.map(async (address) => {
|
|
823
|
-
const has = await globalThis.peernet.has(address, 'block');
|
|
824
|
-
return { has, address };
|
|
825
|
-
}));
|
|
826
|
-
promises = promises.filter(({ status, value }) => status === 'fulfilled' && !value.has);
|
|
827
|
-
await Promise.allSettled(promises.map(({ value }) => this.getAndPutBlock(value.address)));
|
|
828
|
-
}
|
|
829
|
-
if (!this.lastBlock || Number(this.lastBlock.index) < Number(lastBlock.index)) {
|
|
830
|
-
// TODO: check if valid
|
|
831
|
-
const localIndex = this.lastBlock ? this.lastBlock.index : 0;
|
|
832
|
-
const index = lastBlock.index;
|
|
833
|
-
await this.resolveBlock(lastBlock.hash);
|
|
834
|
-
let blocksSynced = localIndex > 0 ? (localIndex > index ? localIndex - index : index - localIndex) : index;
|
|
835
|
-
globalThis.debug(`synced ${blocksSynced} ${blocksSynced > 1 ? 'blocks' : 'block'}`);
|
|
836
|
-
const start = (this.#blocks.length - blocksSynced) - 1;
|
|
837
|
-
if (this.#machine)
|
|
838
|
-
await this.#loadBlocks(this.blocks.slice(start));
|
|
839
|
-
await this.#updateState(new BlockMessage(this.#blocks[this.#blocks.length - 1]));
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
933
|
async #prepareRequest(request) {
|
|
843
934
|
let node = await new globalThis.peernet.protos['peernet-request']({ request });
|
|
844
935
|
return globalThis.peernet.prepareMessage(node);
|
|
@@ -854,12 +945,10 @@ class Chain extends Contract {
|
|
|
854
945
|
return;
|
|
855
946
|
const lastBlock = await this.#makeRequest(peer, 'lastBlock');
|
|
856
947
|
if (Object.keys(lastBlock).length > 0) {
|
|
857
|
-
if (this
|
|
948
|
+
if (!this.lastBlock || !this.blocks[this.blocks.length - 1].loaded || lastBlock && lastBlock.index > this.lastBlock?.index) {
|
|
858
949
|
// this.#knownBlocks = await this.#makeRequest(peer, 'knownBlocks')
|
|
859
|
-
this.#resolveErrored = false;
|
|
860
950
|
await this.syncChain(lastBlock);
|
|
861
|
-
if (await this.hasTransactionToHandle() &&
|
|
862
|
-
this.#runEpoch();
|
|
951
|
+
// if (await this.hasTransactionToHandle() && this.#participating) this.#runEpoch()
|
|
863
952
|
}
|
|
864
953
|
}
|
|
865
954
|
}
|
|
@@ -871,110 +960,10 @@ class Chain extends Contract {
|
|
|
871
960
|
async #versionHandler() {
|
|
872
961
|
return new globalThis.peernet.protos['peernet-response']({ response: { version: this.version } });
|
|
873
962
|
}
|
|
874
|
-
async #lastBlockHandler() {
|
|
875
|
-
return new globalThis.peernet.protos['peernet-response']({ response: { hash: this.#lastBlock?.hash, index: this.#lastBlock?.index } });
|
|
876
|
-
}
|
|
877
|
-
async #knownBlocksHandler() {
|
|
878
|
-
return new globalThis.peernet.protos['peernet-response']({ response: { blocks: this.#blocks.map((block) => block.hash) } });
|
|
879
|
-
}
|
|
880
|
-
async getAndPutBlock(hash) {
|
|
881
|
-
// todo peernet resolves undefined blocks....
|
|
882
|
-
let block = await globalThis.peernet.get(hash, 'block');
|
|
883
|
-
if (block !== undefined) {
|
|
884
|
-
block = await new BlockMessage(block);
|
|
885
|
-
const { index } = block.decoded;
|
|
886
|
-
if (this.#blocks[index] && this.#blocks[index].hash !== block.hash)
|
|
887
|
-
throw `invalid block ${hash} @${index}`;
|
|
888
|
-
if (!await globalThis.peernet.has(hash, 'block'))
|
|
889
|
-
await globalThis.peernet.put(hash, block.encoded, 'block');
|
|
890
|
-
}
|
|
891
|
-
return block;
|
|
892
|
-
}
|
|
893
|
-
async resolveBlock(hash) {
|
|
894
|
-
if (!hash)
|
|
895
|
-
throw new Error(`expected hash, got: ${hash}`);
|
|
896
|
-
if (hash === '0x0')
|
|
897
|
-
return;
|
|
898
|
-
const index = this.#blockHashMap.get(hash);
|
|
899
|
-
if (this.#blocks[index]) {
|
|
900
|
-
if (this.#blocks[index].previousHash !== '0x0') {
|
|
901
|
-
return this.resolveBlock(this.#blocks[index].previousHash);
|
|
902
|
-
}
|
|
903
|
-
else {
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
try {
|
|
908
|
-
const block = await this.getAndPutBlock(hash);
|
|
909
|
-
const { previousHash, index } = block.decoded;
|
|
910
|
-
const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
|
|
911
|
-
this.#totalSize += size;
|
|
912
|
-
this.#blocks[index] = { hash, ...block.decoded };
|
|
913
|
-
this.#blockHashMap.set(hash, index);
|
|
914
|
-
console.log(`resolved block: ${hash} @${index} ${formatBytes(size)}`);
|
|
915
|
-
this.#lastResolved = Date.now();
|
|
916
|
-
if (previousHash !== '0x0') {
|
|
917
|
-
return this.resolveBlock(previousHash);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
catch (error) {
|
|
921
|
-
this.#resolveErrored = true;
|
|
922
|
-
console.error(error);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
async resolveBlocks() {
|
|
926
|
-
try {
|
|
927
|
-
const localBlock = await globalThis.chainStore.get('lastBlock');
|
|
928
|
-
const hash = new TextDecoder().decode(localBlock);
|
|
929
|
-
if (hash && hash !== '0x0')
|
|
930
|
-
await this.resolveBlock(hash);
|
|
931
|
-
this.#lastBlock = this.#blocks[this.#blocks.length - 1];
|
|
932
|
-
}
|
|
933
|
-
catch {
|
|
934
|
-
await globalThis.chainStore.put('lastBlock', new TextEncoder().encode('0x0'));
|
|
935
|
-
return this.resolveBlocks();
|
|
936
|
-
// console.log(e);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
/**
|
|
940
|
-
*
|
|
941
|
-
* @param {Block[]} blocks
|
|
942
|
-
*/
|
|
943
|
-
async #loadBlocks(blocks) {
|
|
944
|
-
let poolTransactionKeys = await globalThis.transactionPoolStore.keys();
|
|
945
|
-
for (const block of blocks) {
|
|
946
|
-
if (block && !block.loaded) {
|
|
947
|
-
for (const transaction of block.transactions) {
|
|
948
|
-
if (poolTransactionKeys.includes(transaction.hash))
|
|
949
|
-
await globalThis.transactionPoolStore.delete(transaction.hash);
|
|
950
|
-
try {
|
|
951
|
-
await this.#machine.execute(transaction.to, transaction.method, transaction.params);
|
|
952
|
-
if (transaction.to === nativeToken) {
|
|
953
|
-
this.#nativeCalls += 1;
|
|
954
|
-
if (transaction.method === 'burn')
|
|
955
|
-
this.#nativeBurns += 1;
|
|
956
|
-
if (transaction.method === 'mint')
|
|
957
|
-
this.#nativeMints += 1;
|
|
958
|
-
if (transaction.method === 'transfer')
|
|
959
|
-
this.#nativeTransfers += 1;
|
|
960
|
-
}
|
|
961
|
-
this.#totalTransactions += 1;
|
|
962
|
-
}
|
|
963
|
-
catch (error) {
|
|
964
|
-
console.log(error);
|
|
965
|
-
return false;
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
this.#blocks[block.index].loaded = true;
|
|
969
|
-
globalThis.debug(`loaded block: ${block.hash} @${block.index}`);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
return true;
|
|
973
|
-
}
|
|
974
963
|
async #executeTransaction({ hash, from, to, method, params, nonce }) {
|
|
975
964
|
try {
|
|
976
|
-
let result = await this
|
|
977
|
-
// if (!result) result = this
|
|
965
|
+
let result = await this.machine.execute(to, method, params);
|
|
966
|
+
// if (!result) result = this.machine.state
|
|
978
967
|
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: 'fulfilled', hash });
|
|
979
968
|
return result || 'no state change';
|
|
980
969
|
}
|
|
@@ -992,7 +981,7 @@ class Chain extends Contract {
|
|
|
992
981
|
const hash = await blockMessage.hash();
|
|
993
982
|
await globalThis.blockStore.put(hash, blockMessage.encoded);
|
|
994
983
|
if (this.lastBlock.index < blockMessage.decoded.index)
|
|
995
|
-
await this
|
|
984
|
+
await this.updateState(blockMessage);
|
|
996
985
|
globalThis.debug(`added block: ${hash}`);
|
|
997
986
|
let promises = [];
|
|
998
987
|
let contracts = [];
|
|
@@ -1014,7 +1003,7 @@ class Chain extends Contract {
|
|
|
1014
1003
|
}
|
|
1015
1004
|
// todo finish state
|
|
1016
1005
|
// for (const contract of contracts) {
|
|
1017
|
-
// const state = await this
|
|
1006
|
+
// const state = await this.machine.get(contract, 'state')
|
|
1018
1007
|
// // await stateStore.put(contract, state)
|
|
1019
1008
|
// console.log(state);
|
|
1020
1009
|
// }
|
|
@@ -1024,12 +1013,6 @@ class Chain extends Contract {
|
|
|
1024
1013
|
console.log({ e: error });
|
|
1025
1014
|
}
|
|
1026
1015
|
}
|
|
1027
|
-
async #updateState(message) {
|
|
1028
|
-
const hash = await message.hash();
|
|
1029
|
-
this.#lastBlock = { hash, ...message.decoded };
|
|
1030
|
-
// await this.state.updateState(message)
|
|
1031
|
-
await globalThis.chainStore.put('lastBlock', hash);
|
|
1032
|
-
}
|
|
1033
1016
|
async participate(address) {
|
|
1034
1017
|
// TODO: validate participant
|
|
1035
1018
|
// hold min amount of 50k ART for 7 days
|
|
@@ -1077,7 +1060,7 @@ class Chain extends Contract {
|
|
|
1077
1060
|
for (let transaction of transactions) {
|
|
1078
1061
|
const hash = await transaction.hash();
|
|
1079
1062
|
const doubleTransactions = [];
|
|
1080
|
-
for (const block of this
|
|
1063
|
+
for (const block of this.blocks) {
|
|
1081
1064
|
for (const transaction of block.transactions) {
|
|
1082
1065
|
if (transaction.hash === hash) {
|
|
1083
1066
|
doubleTransactions.push(hash);
|
|
@@ -1168,7 +1151,7 @@ class Chain extends Contract {
|
|
|
1168
1151
|
let blockMessage = await new BlockMessage(block);
|
|
1169
1152
|
const hash = await blockMessage.hash();
|
|
1170
1153
|
await globalThis.peernet.put(hash, blockMessage.encoded, 'block');
|
|
1171
|
-
await this
|
|
1154
|
+
await this.updateState(blockMessage);
|
|
1172
1155
|
globalThis.debug(`created block: ${hash}`);
|
|
1173
1156
|
globalThis.peernet.publish('add-block', blockMessage.encoded);
|
|
1174
1157
|
globalThis.pubsub.publish('add-block', blockMessage.decoded);
|
|
@@ -1177,7 +1160,7 @@ class Chain extends Contract {
|
|
|
1177
1160
|
console.log(error);
|
|
1178
1161
|
throw new Error(`invalid block ${block}`);
|
|
1179
1162
|
}
|
|
1180
|
-
// data = await this
|
|
1163
|
+
// data = await this.machine.execute(to, method, params)
|
|
1181
1164
|
// transactionStore.put(message.hash, message.encoded)
|
|
1182
1165
|
}
|
|
1183
1166
|
async #sendTransaction(transaction) {
|
|
@@ -1241,7 +1224,7 @@ class Chain extends Contract {
|
|
|
1241
1224
|
*/
|
|
1242
1225
|
internalCall(sender, contract, method, parameters) {
|
|
1243
1226
|
globalThis.msg = this.#createMessage(sender);
|
|
1244
|
-
return this
|
|
1227
|
+
return this.machine.execute(contract, method, parameters);
|
|
1245
1228
|
}
|
|
1246
1229
|
/**
|
|
1247
1230
|
*
|
|
@@ -1252,19 +1235,19 @@ class Chain extends Contract {
|
|
|
1252
1235
|
*/
|
|
1253
1236
|
call(contract, method, parameters) {
|
|
1254
1237
|
globalThis.msg = this.#createMessage();
|
|
1255
|
-
return this
|
|
1238
|
+
return this.machine.execute(contract, method, parameters);
|
|
1256
1239
|
}
|
|
1257
1240
|
staticCall(contract, method, parameters) {
|
|
1258
1241
|
globalThis.msg = this.#createMessage();
|
|
1259
|
-
return this
|
|
1242
|
+
return this.machine.get(contract, method, parameters);
|
|
1260
1243
|
}
|
|
1261
1244
|
delegate(contract, method, parameters) {
|
|
1262
1245
|
globalThis.msg = this.#createMessage();
|
|
1263
|
-
return this
|
|
1246
|
+
return this.machine.execute(contract, method, parameters);
|
|
1264
1247
|
}
|
|
1265
1248
|
staticDelegate(contract, method, parameters) {
|
|
1266
1249
|
globalThis.msg = this.#createMessage();
|
|
1267
|
-
return this
|
|
1250
|
+
return this.machine.get(contract, method, parameters);
|
|
1268
1251
|
}
|
|
1269
1252
|
mint(to, amount) {
|
|
1270
1253
|
return this.call(addresses.nativeToken, 'mint', [to, amount]);
|
|
@@ -1279,7 +1262,7 @@ class Chain extends Contract {
|
|
|
1279
1262
|
return this.staticCall(addresses.contractFactory, 'contracts');
|
|
1280
1263
|
}
|
|
1281
1264
|
deleteAll() {
|
|
1282
|
-
return this
|
|
1265
|
+
return this.machine.deleteAll();
|
|
1283
1266
|
}
|
|
1284
1267
|
/**
|
|
1285
1268
|
* lookup an address for a registered name using the builtin nameService
|