@leofcoin/chain 1.9.3 → 1.9.4
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 +57 -72
- package/exports/browser/{constants-BTdMMS4w.js → constants-D_XqG46B.js} +316 -214
- package/exports/browser/node-browser.js +1 -18
- package/exports/browser/workers/block-worker.js +1 -1
- package/exports/browser/workers/machine-worker.js +1 -1
- package/exports/browser/workers/{worker-BrtyXRJ7-BrtyXRJ7.js → worker-Bsi6vKgF-Bsi6vKgF.js} +238 -198
- package/exports/chain.d.ts +0 -1
- package/exports/chain.js +56 -72
- package/exports/workers/block-worker.js +1 -1
- package/exports/workers/machine-worker.js +1 -1
- package/exports/workers/{worker-BrtyXRJ7-BrtyXRJ7.js → worker-Bsi6vKgF-Bsi6vKgF.js} +238 -198
- package/package.json +2 -2
package/exports/chain.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createDebugger } from '@vandeurenglenn/debug';
|
|
2
2
|
import { formatBytes, jsonStringifyBigInt, jsonParseBigInt, parseUnits, formatUnits } from '@leofcoin/utils';
|
|
3
|
-
import { TransactionMessage, BlockMessage, ContractMessage, LastBlockMessage, BWMessage, BWRequestMessage } from '@leofcoin/messages';
|
|
3
|
+
import { TransactionMessage, BlockMessage, ContractMessage, LastBlockMessage, PrevoteMessage, PrecommitMessage, ProposalMessage, BWMessage, StateMessage, BWRequestMessage } from '@leofcoin/messages';
|
|
4
4
|
import addresses, { contractFactory } from '@leofcoin/addresses';
|
|
5
5
|
import { calculateFee, createContractMessage, signTransaction, contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage } from '@leofcoin/lib';
|
|
6
6
|
import semver from 'semver';
|
|
@@ -8,7 +8,6 @@ import { randombytes } from '@leofcoin/crypto';
|
|
|
8
8
|
import EasyWorker from '@vandeurenglenn/easy-worker';
|
|
9
9
|
import { ContractDeploymentError, ExecutionError, isResolveError, ResolveError, isExecutionError } from '@leofcoin/errors';
|
|
10
10
|
import { log } from 'console';
|
|
11
|
-
import codecs from '@leofcoin/codecs/utils';
|
|
12
11
|
import { P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO } from './constants-eo0U5-D_.js';
|
|
13
12
|
import { log as log$1 } from 'node:console';
|
|
14
13
|
import '@leofcoin/networks';
|
|
@@ -1157,11 +1156,6 @@ class Jobber {
|
|
|
1157
1156
|
}
|
|
1158
1157
|
}
|
|
1159
1158
|
|
|
1160
|
-
codecs.addCodec({
|
|
1161
|
-
name: 'last-block-message',
|
|
1162
|
-
codec: 0x6c626d,
|
|
1163
|
-
hashAlg: 'keccak-256'
|
|
1164
|
-
});
|
|
1165
1159
|
const debug$1 = createDebugger('leofcoin/state');
|
|
1166
1160
|
class State extends Contract {
|
|
1167
1161
|
#resolveErrored;
|
|
@@ -2348,16 +2342,6 @@ class ConnectionMonitor {
|
|
|
2348
2342
|
}
|
|
2349
2343
|
}
|
|
2350
2344
|
|
|
2351
|
-
codecs.addCodec({
|
|
2352
|
-
name: 'last-block-message',
|
|
2353
|
-
codec: 0x6c626d,
|
|
2354
|
-
hashAlg: 'keccak-256'
|
|
2355
|
-
});
|
|
2356
|
-
codecs.addCodec({
|
|
2357
|
-
name: 'last-block-request-message',
|
|
2358
|
-
codec: 0x6c62726d,
|
|
2359
|
-
hashAlg: 'keccak-256'
|
|
2360
|
-
});
|
|
2361
2345
|
const debug = createDebugger('leofcoin/chain');
|
|
2362
2346
|
// check if browser or local
|
|
2363
2347
|
class Chain extends VersionControl {
|
|
@@ -2451,7 +2435,10 @@ class Chain extends VersionControl {
|
|
|
2451
2435
|
return;
|
|
2452
2436
|
this.#castedVotes.add(voteKey);
|
|
2453
2437
|
const from = peernet.selectedAccount;
|
|
2454
|
-
const
|
|
2438
|
+
const voteData = { blockHash, index: BigInt(index), round: BigInt(round), from };
|
|
2439
|
+
const Message = type === 'prevote' ? PrevoteMessage : PrecommitMessage;
|
|
2440
|
+
const message = new Message(voteData);
|
|
2441
|
+
const payload = message.encoded;
|
|
2455
2442
|
try {
|
|
2456
2443
|
globalThis.peernet.publish(`consensus:${type}`, payload);
|
|
2457
2444
|
}
|
|
@@ -2466,16 +2453,17 @@ class Chain extends VersionControl {
|
|
|
2466
2453
|
*/
|
|
2467
2454
|
this.#handleProposal = async (payload) => {
|
|
2468
2455
|
try {
|
|
2469
|
-
const
|
|
2456
|
+
const message = new ProposalMessage(payload);
|
|
2457
|
+
const msg = message.decoded;
|
|
2470
2458
|
const { blockHash, index, round, from } = msg;
|
|
2471
|
-
const validators = await this.#getConsensusValidators(index);
|
|
2472
|
-
const expectedProposerIdx = (index + round) % validators.length;
|
|
2459
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2460
|
+
const expectedProposerIdx = Number((index + round) % BigInt(validators.length));
|
|
2473
2461
|
if (!validators[expectedProposerIdx] || validators[expectedProposerIdx] !== from) {
|
|
2474
2462
|
debug(`[consensus] Proposal from wrong proposer at height ${index} round ${round}`);
|
|
2475
2463
|
return;
|
|
2476
2464
|
}
|
|
2477
2465
|
const localBlock = await this.lastBlock;
|
|
2478
|
-
const localIndex = localBlock?.index !== undefined ?
|
|
2466
|
+
const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
|
|
2479
2467
|
if (index <= localIndex) {
|
|
2480
2468
|
debug(`[consensus] Ignoring stale proposal at height ${index} (local: ${localIndex})`);
|
|
2481
2469
|
return;
|
|
@@ -2494,13 +2482,13 @@ class Chain extends VersionControl {
|
|
|
2494
2482
|
debug(`[consensus] Cannot fetch proposed block ${blockHash}:`, e?.message);
|
|
2495
2483
|
return;
|
|
2496
2484
|
}
|
|
2497
|
-
this.#consensusRound = round;
|
|
2485
|
+
this.#consensusRound = Number(round);
|
|
2498
2486
|
if (this.#roundTimer) {
|
|
2499
2487
|
clearTimeout(this.#roundTimer);
|
|
2500
2488
|
this.#roundTimer = null;
|
|
2501
2489
|
}
|
|
2502
2490
|
if (validators.includes(peernet.selectedAccount) && !this.#isJailed(peernet.selectedAccount)) {
|
|
2503
|
-
await this.#castVote('prevote', blockHash, index, round);
|
|
2491
|
+
await this.#castVote('prevote', blockHash, Number(index), Number(round));
|
|
2504
2492
|
}
|
|
2505
2493
|
}
|
|
2506
2494
|
catch (e) {
|
|
@@ -2513,13 +2501,14 @@ class Chain extends VersionControl {
|
|
|
2513
2501
|
*/
|
|
2514
2502
|
this.#handlePrevote = async (payload) => {
|
|
2515
2503
|
try {
|
|
2516
|
-
const
|
|
2504
|
+
const message = new PrevoteMessage(payload);
|
|
2505
|
+
const msg = message.decoded;
|
|
2517
2506
|
const { blockHash, index, round, from } = msg;
|
|
2518
|
-
const validators = await this.#getConsensusValidators(index);
|
|
2507
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2519
2508
|
if (!validators.includes(from))
|
|
2520
2509
|
return;
|
|
2521
2510
|
const localBlock = await this.lastBlock;
|
|
2522
|
-
const localIndex = localBlock?.index !== undefined ?
|
|
2511
|
+
const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
|
|
2523
2512
|
if (index <= localIndex)
|
|
2524
2513
|
return;
|
|
2525
2514
|
const voteKey = `${index}:${round}:${blockHash}`;
|
|
@@ -2532,7 +2521,7 @@ class Chain extends VersionControl {
|
|
|
2532
2521
|
if (voteCount >= threshold &&
|
|
2533
2522
|
validators.includes(peernet.selectedAccount) &&
|
|
2534
2523
|
!this.#isJailed(peernet.selectedAccount)) {
|
|
2535
|
-
await this.#castVote('precommit', blockHash, index, round);
|
|
2524
|
+
await this.#castVote('precommit', blockHash, Number(index), Number(round));
|
|
2536
2525
|
}
|
|
2537
2526
|
}
|
|
2538
2527
|
catch (e) {
|
|
@@ -2546,12 +2535,13 @@ class Chain extends VersionControl {
|
|
|
2546
2535
|
*/
|
|
2547
2536
|
this.#handlePrecommit = async (payload) => {
|
|
2548
2537
|
try {
|
|
2549
|
-
const
|
|
2538
|
+
const message = new PrecommitMessage(payload);
|
|
2539
|
+
const msg = message.decoded;
|
|
2550
2540
|
const { blockHash, index, round, from } = msg;
|
|
2551
|
-
const validators = await this.#getConsensusValidators(index);
|
|
2541
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2552
2542
|
if (!validators.includes(from))
|
|
2553
2543
|
return;
|
|
2554
|
-
if (index <= this.#committedHeight)
|
|
2544
|
+
if (index <= BigInt(this.#committedHeight))
|
|
2555
2545
|
return;
|
|
2556
2546
|
const voteKey = `${index}:${round}:${blockHash}`;
|
|
2557
2547
|
if (!this.#precommits.has(voteKey))
|
|
@@ -2560,27 +2550,27 @@ class Chain extends VersionControl {
|
|
|
2560
2550
|
const threshold = Math.ceil((2 * validators.length) / 3);
|
|
2561
2551
|
const voteCount = this.#precommits.get(voteKey).size;
|
|
2562
2552
|
debug(`[consensus] Precommits ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`);
|
|
2563
|
-
if (voteCount >= threshold && index > this.#committedHeight) {
|
|
2564
|
-
this.#committedHeight = index;
|
|
2553
|
+
if (voteCount >= threshold && index > BigInt(this.#committedHeight)) {
|
|
2554
|
+
this.#committedHeight = Number(index);
|
|
2565
2555
|
this.#consensusRound = 0;
|
|
2566
2556
|
// Prune vote state for committed and older heights
|
|
2567
2557
|
for (const key of [...this.#prevotes.keys()]) {
|
|
2568
|
-
if (
|
|
2558
|
+
if (BigInt(key.split(':')[0]) <= index)
|
|
2569
2559
|
this.#prevotes.delete(key);
|
|
2570
2560
|
}
|
|
2571
2561
|
for (const key of [...this.#precommits.keys()]) {
|
|
2572
|
-
if (
|
|
2562
|
+
if (BigInt(key.split(':')[0]) <= index)
|
|
2573
2563
|
this.#precommits.delete(key);
|
|
2574
2564
|
}
|
|
2575
2565
|
for (const key of [...this.#castedVotes]) {
|
|
2576
|
-
if (
|
|
2566
|
+
if (BigInt(key.split(':')[1]) <= index)
|
|
2577
2567
|
this.#castedVotes.delete(key);
|
|
2578
2568
|
}
|
|
2579
2569
|
// Non-proposers add the block to local state now.
|
|
2580
2570
|
// Proposers already committed state in #createBlock() and their
|
|
2581
2571
|
// lastBlock.index === index, so the guard below skips them.
|
|
2582
2572
|
const currentBlock = await this.lastBlock;
|
|
2583
|
-
const currentIndex = currentBlock?.index !== undefined ?
|
|
2573
|
+
const currentIndex = currentBlock?.index !== undefined ? currentBlock.index : -1n;
|
|
2584
2574
|
if (index > currentIndex) {
|
|
2585
2575
|
debug(`[consensus] ✅ Committing block ${blockHash} at height ${index}`);
|
|
2586
2576
|
try {
|
|
@@ -2853,7 +2843,9 @@ class Chain extends VersionControl {
|
|
|
2853
2843
|
await globalThis.peernet.addRequestHandler('transactionPool', this.#transactionPoolHandler.bind(this));
|
|
2854
2844
|
await globalThis.peernet.addRequestHandler('version', this.#versionHandler.bind(this));
|
|
2855
2845
|
await globalThis.peernet.addRequestHandler('stateInfo', () => {
|
|
2856
|
-
return new globalThis.peernet.protos['peernet-response']({
|
|
2846
|
+
return new globalThis.peernet.protos['peernet-response']({
|
|
2847
|
+
response: new StateMessage(this.machine.states.info).encoded
|
|
2848
|
+
});
|
|
2857
2849
|
});
|
|
2858
2850
|
globalThis.peernet.subscribe('add-block', this.#addBlock.bind(this));
|
|
2859
2851
|
globalThis.peernet.subscribe('invalid-transaction', this.#invalidTransaction.bind(this));
|
|
@@ -2941,13 +2933,8 @@ class Chain extends VersionControl {
|
|
|
2941
2933
|
async getPeerTransactionPool(peer) {
|
|
2942
2934
|
let transactionsInPool = await this.#makeRequest(peer, 'transactionPool');
|
|
2943
2935
|
if (transactionsInPool instanceof Uint8Array) {
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
transactionsInPool = JSON.parse(text);
|
|
2947
|
-
}
|
|
2948
|
-
catch (e) {
|
|
2949
|
-
return [];
|
|
2950
|
-
}
|
|
2936
|
+
debug('transactionPool response must be decoded array payload');
|
|
2937
|
+
return [];
|
|
2951
2938
|
}
|
|
2952
2939
|
if (!Array.isArray(transactionsInPool))
|
|
2953
2940
|
return [];
|
|
@@ -2979,13 +2966,7 @@ class Chain extends VersionControl {
|
|
|
2979
2966
|
try {
|
|
2980
2967
|
let versionResponse = await this.#makeRequest(peer, 'version');
|
|
2981
2968
|
if (versionResponse instanceof Uint8Array) {
|
|
2982
|
-
|
|
2983
|
-
try {
|
|
2984
|
-
versionResponse = JSON.parse(decoded);
|
|
2985
|
-
}
|
|
2986
|
-
catch {
|
|
2987
|
-
versionResponse = decoded;
|
|
2988
|
-
}
|
|
2969
|
+
versionResponse = new TextDecoder().decode(versionResponse);
|
|
2989
2970
|
}
|
|
2990
2971
|
if (typeof versionResponse === 'string') {
|
|
2991
2972
|
peer.version = versionResponse;
|
|
@@ -2995,6 +2976,12 @@ class Chain extends VersionControl {
|
|
|
2995
2976
|
typeof versionResponse.version === 'string') {
|
|
2996
2977
|
peer.version = versionResponse.version;
|
|
2997
2978
|
}
|
|
2979
|
+
if (!peer.version || typeof peer.version !== 'string') {
|
|
2980
|
+
const reason = `invalid version response from peer ${peerId}`;
|
|
2981
|
+
debug(reason);
|
|
2982
|
+
await this.#recordPeerFailure(peerId, reason);
|
|
2983
|
+
return;
|
|
2984
|
+
}
|
|
2998
2985
|
}
|
|
2999
2986
|
catch (error) {
|
|
3000
2987
|
debug(`failed to request version from peer ${peerId}:`, error?.message ?? error);
|
|
@@ -3003,7 +2990,9 @@ class Chain extends VersionControl {
|
|
|
3003
2990
|
}
|
|
3004
2991
|
debug(`peer connected with version ${peer.version}`);
|
|
3005
2992
|
if (!this.isVersionCompatible(peer.version)) {
|
|
3006
|
-
|
|
2993
|
+
const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})`;
|
|
2994
|
+
console.error(`[chain] ${mismatchReason}`);
|
|
2995
|
+
await this.#recordPeerFailure(peerId, mismatchReason);
|
|
3007
2996
|
return;
|
|
3008
2997
|
}
|
|
3009
2998
|
let lastBlock;
|
|
@@ -3023,7 +3012,7 @@ class Chain extends VersionControl {
|
|
|
3023
3012
|
// This prevents Byzantine nodes from claiming a fake chain length to steer our sync
|
|
3024
3013
|
const localBlock = await this.lastBlock;
|
|
3025
3014
|
const MAX_SYNC_AHEAD = 100_000;
|
|
3026
|
-
if (lastBlock?.index > (localBlock?.index ?? 0) + MAX_SYNC_AHEAD) {
|
|
3015
|
+
if (lastBlock?.index > BigInt(localBlock?.index ?? 0) + BigInt(MAX_SYNC_AHEAD)) {
|
|
3027
3016
|
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || 'unknown';
|
|
3028
3017
|
debug(`Peer ${peerName} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`);
|
|
3029
3018
|
await this.#recordPeerFailure(peerId, `unreasonable lastBlock index: ${lastBlock.index}`);
|
|
@@ -3039,15 +3028,13 @@ class Chain extends VersionControl {
|
|
|
3039
3028
|
try {
|
|
3040
3029
|
let knownBlocksResponse = await this.#makeRequest(peer, 'knownBlocks');
|
|
3041
3030
|
if (knownBlocksResponse instanceof Uint8Array) {
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
console.log(e);
|
|
3047
|
-
}
|
|
3031
|
+
const reason = `knownBlocks must be object response, got raw bytes from ${peerId}`;
|
|
3032
|
+
debug(reason);
|
|
3033
|
+
await this.#recordPeerFailure(peerId, reason);
|
|
3034
|
+
return;
|
|
3048
3035
|
}
|
|
3049
3036
|
const MAX_WANTLIST_SIZE = 1000;
|
|
3050
|
-
if (knownBlocksResponse.blocks) {
|
|
3037
|
+
if (knownBlocksResponse && Array.isArray(knownBlocksResponse.blocks)) {
|
|
3051
3038
|
const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
|
|
3052
3039
|
if (remaining > 0) {
|
|
3053
3040
|
for (const hash of knownBlocksResponse.blocks.slice(0, remaining)) {
|
|
@@ -3089,12 +3076,7 @@ class Chain extends VersionControl {
|
|
|
3089
3076
|
try {
|
|
3090
3077
|
let stateInfo = await this.#makeRequest(peer, 'stateInfo');
|
|
3091
3078
|
if (stateInfo instanceof Uint8Array) {
|
|
3092
|
-
|
|
3093
|
-
stateInfo = JSON.parse(new TextDecoder().decode(stateInfo));
|
|
3094
|
-
}
|
|
3095
|
-
catch (e) {
|
|
3096
|
-
console.log(e);
|
|
3097
|
-
}
|
|
3079
|
+
stateInfo = new StateMessage(stateInfo).decoded;
|
|
3098
3080
|
}
|
|
3099
3081
|
await this.syncChain(lastBlock);
|
|
3100
3082
|
this.machine.states.info = stateInfo;
|
|
@@ -3112,7 +3094,7 @@ class Chain extends VersionControl {
|
|
|
3112
3094
|
return new globalThis.peernet.protos['peernet-response']({ response: pool });
|
|
3113
3095
|
}
|
|
3114
3096
|
async #versionHandler() {
|
|
3115
|
-
return new globalThis.peernet.protos['peernet-response']({ response:
|
|
3097
|
+
return new globalThis.peernet.protos['peernet-response']({ response: this.version });
|
|
3116
3098
|
}
|
|
3117
3099
|
async #executeTransaction({ hash, from, to, method, params, nonce }) {
|
|
3118
3100
|
try {
|
|
@@ -3464,12 +3446,14 @@ class Chain extends VersionControl {
|
|
|
3464
3446
|
debug(`created block: ${hash} @${block.index}`);
|
|
3465
3447
|
// Phase 2: announce proposal for consensus voting instead of direct add-block
|
|
3466
3448
|
console.log(`[consensus] 📤 Proposing block #${block.index} | hash: ${hash} | round: ${this.#consensusRound}`);
|
|
3467
|
-
const
|
|
3449
|
+
const proposalData = {
|
|
3468
3450
|
blockHash: hash,
|
|
3469
|
-
index: block.index,
|
|
3470
|
-
round: this.#consensusRound,
|
|
3451
|
+
index: BigInt(block.index),
|
|
3452
|
+
round: BigInt(this.#consensusRound),
|
|
3471
3453
|
from: peernet.selectedAccount
|
|
3472
|
-
}
|
|
3454
|
+
};
|
|
3455
|
+
const proposalMessage = new ProposalMessage(proposalData);
|
|
3456
|
+
const proposalPayload = proposalMessage.encoded;
|
|
3473
3457
|
try {
|
|
3474
3458
|
globalThis.peernet.publish('consensus:propose', proposalPayload);
|
|
3475
3459
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as EasyWorker, C as ContractMessage, T as TransactionMessage } from './worker-
|
|
1
|
+
import { E as EasyWorker, C as ContractMessage, T as TransactionMessage } from './worker-Bsi6vKgF-Bsi6vKgF.js';
|
|
2
2
|
|
|
3
3
|
/* Do NOT modify this file; see /src.ts/_admin/update-version.ts */
|
|
4
4
|
/**
|