@leofcoin/chain 1.9.2 → 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 +111 -89
- 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 +110 -89
- 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 +9 -9
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';
|
|
@@ -154,11 +153,25 @@ class Transaction extends Protocol {
|
|
|
154
153
|
return Number(nonce);
|
|
155
154
|
}
|
|
156
155
|
async validateNonce(address, nonce) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
156
|
+
// Compare only against the COMMITTED nonce (accountsStore), not the pool max.
|
|
157
|
+
// The pool may hold many future nonces from batch sends — rejecting lower nonces
|
|
158
|
+
// because a higher one is already queued would break concurrent batch submission.
|
|
159
|
+
let committedNonce;
|
|
160
|
+
try {
|
|
161
|
+
if (await globalThis.accountsStore.has(address)) {
|
|
162
|
+
const raw = await globalThis.accountsStore.get(address);
|
|
163
|
+
committedNonce = Number(new TextDecoder().decode(raw));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
committedNonce = await this.#getNonceFallback(address);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
committedNonce = 0;
|
|
171
|
+
}
|
|
172
|
+
if (committedNonce >= nonce)
|
|
161
173
|
throw new Error(`a transaction with the same nonce already exists`);
|
|
174
|
+
// Only reject exact duplicates already in the pool (not "higher nonce" rejections)
|
|
162
175
|
let transactions = await globalThis.transactionPoolStore.values();
|
|
163
176
|
transactions = await this.promiseTransactions(transactions);
|
|
164
177
|
transactions = transactions.filter((tx) => tx.decoded.from === address);
|
|
@@ -1143,11 +1156,6 @@ class Jobber {
|
|
|
1143
1156
|
}
|
|
1144
1157
|
}
|
|
1145
1158
|
|
|
1146
|
-
codecs.addCodec({
|
|
1147
|
-
name: 'last-block-message',
|
|
1148
|
-
codec: 0x6c626d,
|
|
1149
|
-
hashAlg: 'keccak-256'
|
|
1150
|
-
});
|
|
1151
1159
|
const debug$1 = createDebugger('leofcoin/state');
|
|
1152
1160
|
class State extends Contract {
|
|
1153
1161
|
#resolveErrored;
|
|
@@ -1709,6 +1717,8 @@ class State extends Contract {
|
|
|
1709
1717
|
}
|
|
1710
1718
|
async #getLatestBlock() {
|
|
1711
1719
|
let promises = [];
|
|
1720
|
+
const connectedPeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected);
|
|
1721
|
+
let compatiblePeerCount = 0;
|
|
1712
1722
|
let data = await new globalThis.peernet.protos['peernet-request']({
|
|
1713
1723
|
request: 'lastBlock'
|
|
1714
1724
|
});
|
|
@@ -1716,15 +1726,8 @@ class State extends Contract {
|
|
|
1716
1726
|
for (const id in globalThis.peernet.connections) {
|
|
1717
1727
|
// @ts-ignore
|
|
1718
1728
|
const peer = globalThis.peernet.connections[id];
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
if (!peer.version || !this.version)
|
|
1722
|
-
return false;
|
|
1723
|
-
const [peerMajor, peerMinor] = peer.version.split('.');
|
|
1724
|
-
const [localMajor, localMinor] = this.version.split('.');
|
|
1725
|
-
return peerMajor === localMajor && peerMinor === localMinor;
|
|
1726
|
-
};
|
|
1727
|
-
if (peer.connected && isVersionCompatible()) {
|
|
1729
|
+
if (peer.connected && this.isVersionCompatible(peer.version)) {
|
|
1730
|
+
compatiblePeerCount += 1;
|
|
1728
1731
|
const task = async () => {
|
|
1729
1732
|
try {
|
|
1730
1733
|
const result = await peer.request(node.encoded);
|
|
@@ -1741,9 +1744,15 @@ class State extends Contract {
|
|
|
1741
1744
|
promises.push(task());
|
|
1742
1745
|
}
|
|
1743
1746
|
}
|
|
1747
|
+
if (connectedPeers.length > 0 && compatiblePeerCount === 0) {
|
|
1748
|
+
throw new ResolveError(`latestBlock: no compatible peers found for local version ${this.version} among ${connectedPeers.length} connected peers`);
|
|
1749
|
+
}
|
|
1744
1750
|
// @ts-ignore
|
|
1745
1751
|
console.log({ promises });
|
|
1746
1752
|
promises = (await this.promiseRequests(promises));
|
|
1753
|
+
if (compatiblePeerCount > 0 && promises.length === 0) {
|
|
1754
|
+
throw new ResolveError('latestBlock: no responses from compatible peers');
|
|
1755
|
+
}
|
|
1747
1756
|
console.log({ promises });
|
|
1748
1757
|
let latest = { index: 0, hash: '0x0', previousHash: '0x0' };
|
|
1749
1758
|
promises = promises.sort((a, b) => b.index - a.index);
|
|
@@ -1758,15 +1767,7 @@ class State extends Contract {
|
|
|
1758
1767
|
throw new Error('invalid block @getLatestBlock');
|
|
1759
1768
|
latest = { ...message.decoded, hash };
|
|
1760
1769
|
const peer = promises[0].peer;
|
|
1761
|
-
|
|
1762
|
-
const isVersionCompatible = () => {
|
|
1763
|
-
if (!peer.version || !this.version)
|
|
1764
|
-
return false;
|
|
1765
|
-
const [peerMajor, peerMinor] = peer.version.split('.');
|
|
1766
|
-
const [localMajor, localMinor] = this.version.split('.');
|
|
1767
|
-
return peerMajor === localMajor && peerMinor === localMinor;
|
|
1768
|
-
};
|
|
1769
|
-
if (peer.connected && isVersionCompatible()) {
|
|
1770
|
+
if (peer.connected && this.isVersionCompatible(peer.version)) {
|
|
1770
1771
|
let data = await new globalThis.peernet.protos['peernet-request']({
|
|
1771
1772
|
request: 'knownBlocks'
|
|
1772
1773
|
});
|
|
@@ -1914,7 +1915,7 @@ class State extends Contract {
|
|
|
1914
1915
|
if (this.#chainSyncing)
|
|
1915
1916
|
return false;
|
|
1916
1917
|
// Check if we have any connected peers with the same version
|
|
1917
|
-
const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version
|
|
1918
|
+
const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && this.isVersionCompatible(peer.version));
|
|
1918
1919
|
if (compatiblePeers.length === 0) {
|
|
1919
1920
|
debug$1('No compatible peers available for sync');
|
|
1920
1921
|
return false;
|
|
@@ -1930,7 +1931,7 @@ class State extends Contract {
|
|
|
1930
1931
|
async #waitForPeers(timeoutMs = 30000) {
|
|
1931
1932
|
return new Promise((resolve) => {
|
|
1932
1933
|
const checkPeers = () => {
|
|
1933
|
-
const peers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version
|
|
1934
|
+
const peers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && this.isVersionCompatible(peer.version));
|
|
1934
1935
|
if (peers.length > 0) {
|
|
1935
1936
|
resolve(true);
|
|
1936
1937
|
}
|
|
@@ -2341,16 +2342,6 @@ class ConnectionMonitor {
|
|
|
2341
2342
|
}
|
|
2342
2343
|
}
|
|
2343
2344
|
|
|
2344
|
-
codecs.addCodec({
|
|
2345
|
-
name: 'last-block-message',
|
|
2346
|
-
codec: 0x6c626d,
|
|
2347
|
-
hashAlg: 'keccak-256'
|
|
2348
|
-
});
|
|
2349
|
-
codecs.addCodec({
|
|
2350
|
-
name: 'last-block-request-message',
|
|
2351
|
-
codec: 0x6c62726d,
|
|
2352
|
-
hashAlg: 'keccak-256'
|
|
2353
|
-
});
|
|
2354
2345
|
const debug = createDebugger('leofcoin/chain');
|
|
2355
2346
|
// check if browser or local
|
|
2356
2347
|
class Chain extends VersionControl {
|
|
@@ -2444,7 +2435,10 @@ class Chain extends VersionControl {
|
|
|
2444
2435
|
return;
|
|
2445
2436
|
this.#castedVotes.add(voteKey);
|
|
2446
2437
|
const from = peernet.selectedAccount;
|
|
2447
|
-
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;
|
|
2448
2442
|
try {
|
|
2449
2443
|
globalThis.peernet.publish(`consensus:${type}`, payload);
|
|
2450
2444
|
}
|
|
@@ -2459,16 +2453,17 @@ class Chain extends VersionControl {
|
|
|
2459
2453
|
*/
|
|
2460
2454
|
this.#handleProposal = async (payload) => {
|
|
2461
2455
|
try {
|
|
2462
|
-
const
|
|
2456
|
+
const message = new ProposalMessage(payload);
|
|
2457
|
+
const msg = message.decoded;
|
|
2463
2458
|
const { blockHash, index, round, from } = msg;
|
|
2464
|
-
const validators = await this.#getConsensusValidators(index);
|
|
2465
|
-
const expectedProposerIdx = (index + round) % validators.length;
|
|
2459
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2460
|
+
const expectedProposerIdx = Number((index + round) % BigInt(validators.length));
|
|
2466
2461
|
if (!validators[expectedProposerIdx] || validators[expectedProposerIdx] !== from) {
|
|
2467
2462
|
debug(`[consensus] Proposal from wrong proposer at height ${index} round ${round}`);
|
|
2468
2463
|
return;
|
|
2469
2464
|
}
|
|
2470
2465
|
const localBlock = await this.lastBlock;
|
|
2471
|
-
const localIndex = localBlock?.index !== undefined ?
|
|
2466
|
+
const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
|
|
2472
2467
|
if (index <= localIndex) {
|
|
2473
2468
|
debug(`[consensus] Ignoring stale proposal at height ${index} (local: ${localIndex})`);
|
|
2474
2469
|
return;
|
|
@@ -2487,13 +2482,13 @@ class Chain extends VersionControl {
|
|
|
2487
2482
|
debug(`[consensus] Cannot fetch proposed block ${blockHash}:`, e?.message);
|
|
2488
2483
|
return;
|
|
2489
2484
|
}
|
|
2490
|
-
this.#consensusRound = round;
|
|
2485
|
+
this.#consensusRound = Number(round);
|
|
2491
2486
|
if (this.#roundTimer) {
|
|
2492
2487
|
clearTimeout(this.#roundTimer);
|
|
2493
2488
|
this.#roundTimer = null;
|
|
2494
2489
|
}
|
|
2495
2490
|
if (validators.includes(peernet.selectedAccount) && !this.#isJailed(peernet.selectedAccount)) {
|
|
2496
|
-
await this.#castVote('prevote', blockHash, index, round);
|
|
2491
|
+
await this.#castVote('prevote', blockHash, Number(index), Number(round));
|
|
2497
2492
|
}
|
|
2498
2493
|
}
|
|
2499
2494
|
catch (e) {
|
|
@@ -2506,13 +2501,14 @@ class Chain extends VersionControl {
|
|
|
2506
2501
|
*/
|
|
2507
2502
|
this.#handlePrevote = async (payload) => {
|
|
2508
2503
|
try {
|
|
2509
|
-
const
|
|
2504
|
+
const message = new PrevoteMessage(payload);
|
|
2505
|
+
const msg = message.decoded;
|
|
2510
2506
|
const { blockHash, index, round, from } = msg;
|
|
2511
|
-
const validators = await this.#getConsensusValidators(index);
|
|
2507
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2512
2508
|
if (!validators.includes(from))
|
|
2513
2509
|
return;
|
|
2514
2510
|
const localBlock = await this.lastBlock;
|
|
2515
|
-
const localIndex = localBlock?.index !== undefined ?
|
|
2511
|
+
const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
|
|
2516
2512
|
if (index <= localIndex)
|
|
2517
2513
|
return;
|
|
2518
2514
|
const voteKey = `${index}:${round}:${blockHash}`;
|
|
@@ -2525,7 +2521,7 @@ class Chain extends VersionControl {
|
|
|
2525
2521
|
if (voteCount >= threshold &&
|
|
2526
2522
|
validators.includes(peernet.selectedAccount) &&
|
|
2527
2523
|
!this.#isJailed(peernet.selectedAccount)) {
|
|
2528
|
-
await this.#castVote('precommit', blockHash, index, round);
|
|
2524
|
+
await this.#castVote('precommit', blockHash, Number(index), Number(round));
|
|
2529
2525
|
}
|
|
2530
2526
|
}
|
|
2531
2527
|
catch (e) {
|
|
@@ -2539,12 +2535,13 @@ class Chain extends VersionControl {
|
|
|
2539
2535
|
*/
|
|
2540
2536
|
this.#handlePrecommit = async (payload) => {
|
|
2541
2537
|
try {
|
|
2542
|
-
const
|
|
2538
|
+
const message = new PrecommitMessage(payload);
|
|
2539
|
+
const msg = message.decoded;
|
|
2543
2540
|
const { blockHash, index, round, from } = msg;
|
|
2544
|
-
const validators = await this.#getConsensusValidators(index);
|
|
2541
|
+
const validators = await this.#getConsensusValidators(Number(index));
|
|
2545
2542
|
if (!validators.includes(from))
|
|
2546
2543
|
return;
|
|
2547
|
-
if (index <= this.#committedHeight)
|
|
2544
|
+
if (index <= BigInt(this.#committedHeight))
|
|
2548
2545
|
return;
|
|
2549
2546
|
const voteKey = `${index}:${round}:${blockHash}`;
|
|
2550
2547
|
if (!this.#precommits.has(voteKey))
|
|
@@ -2553,27 +2550,27 @@ class Chain extends VersionControl {
|
|
|
2553
2550
|
const threshold = Math.ceil((2 * validators.length) / 3);
|
|
2554
2551
|
const voteCount = this.#precommits.get(voteKey).size;
|
|
2555
2552
|
debug(`[consensus] Precommits ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`);
|
|
2556
|
-
if (voteCount >= threshold && index > this.#committedHeight) {
|
|
2557
|
-
this.#committedHeight = index;
|
|
2553
|
+
if (voteCount >= threshold && index > BigInt(this.#committedHeight)) {
|
|
2554
|
+
this.#committedHeight = Number(index);
|
|
2558
2555
|
this.#consensusRound = 0;
|
|
2559
2556
|
// Prune vote state for committed and older heights
|
|
2560
2557
|
for (const key of [...this.#prevotes.keys()]) {
|
|
2561
|
-
if (
|
|
2558
|
+
if (BigInt(key.split(':')[0]) <= index)
|
|
2562
2559
|
this.#prevotes.delete(key);
|
|
2563
2560
|
}
|
|
2564
2561
|
for (const key of [...this.#precommits.keys()]) {
|
|
2565
|
-
if (
|
|
2562
|
+
if (BigInt(key.split(':')[0]) <= index)
|
|
2566
2563
|
this.#precommits.delete(key);
|
|
2567
2564
|
}
|
|
2568
2565
|
for (const key of [...this.#castedVotes]) {
|
|
2569
|
-
if (
|
|
2566
|
+
if (BigInt(key.split(':')[1]) <= index)
|
|
2570
2567
|
this.#castedVotes.delete(key);
|
|
2571
2568
|
}
|
|
2572
2569
|
// Non-proposers add the block to local state now.
|
|
2573
2570
|
// Proposers already committed state in #createBlock() and their
|
|
2574
2571
|
// lastBlock.index === index, so the guard below skips them.
|
|
2575
2572
|
const currentBlock = await this.lastBlock;
|
|
2576
|
-
const currentIndex = currentBlock?.index !== undefined ?
|
|
2573
|
+
const currentIndex = currentBlock?.index !== undefined ? currentBlock.index : -1n;
|
|
2577
2574
|
if (index > currentIndex) {
|
|
2578
2575
|
debug(`[consensus] ✅ Committing block ${blockHash} at height ${index}`);
|
|
2579
2576
|
try {
|
|
@@ -2846,7 +2843,9 @@ class Chain extends VersionControl {
|
|
|
2846
2843
|
await globalThis.peernet.addRequestHandler('transactionPool', this.#transactionPoolHandler.bind(this));
|
|
2847
2844
|
await globalThis.peernet.addRequestHandler('version', this.#versionHandler.bind(this));
|
|
2848
2845
|
await globalThis.peernet.addRequestHandler('stateInfo', () => {
|
|
2849
|
-
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
|
+
});
|
|
2850
2849
|
});
|
|
2851
2850
|
globalThis.peernet.subscribe('add-block', this.#addBlock.bind(this));
|
|
2852
2851
|
globalThis.peernet.subscribe('invalid-transaction', this.#invalidTransaction.bind(this));
|
|
@@ -2934,13 +2933,8 @@ class Chain extends VersionControl {
|
|
|
2934
2933
|
async getPeerTransactionPool(peer) {
|
|
2935
2934
|
let transactionsInPool = await this.#makeRequest(peer, 'transactionPool');
|
|
2936
2935
|
if (transactionsInPool instanceof Uint8Array) {
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
transactionsInPool = JSON.parse(text);
|
|
2940
|
-
}
|
|
2941
|
-
catch (e) {
|
|
2942
|
-
return [];
|
|
2943
|
-
}
|
|
2936
|
+
debug('transactionPool response must be decoded array payload');
|
|
2937
|
+
return [];
|
|
2944
2938
|
}
|
|
2945
2939
|
if (!Array.isArray(transactionsInPool))
|
|
2946
2940
|
return [];
|
|
@@ -2964,9 +2958,41 @@ class Chain extends VersionControl {
|
|
|
2964
2958
|
async #peerConnected(peerId) {
|
|
2965
2959
|
debug(`peer connected: ${peerId}`);
|
|
2966
2960
|
const peer = peernet.getConnection(peerId);
|
|
2961
|
+
if (!peer) {
|
|
2962
|
+
debug(`peer not found: ${peerId}`);
|
|
2963
|
+
return;
|
|
2964
|
+
}
|
|
2965
|
+
if (!peer.version) {
|
|
2966
|
+
try {
|
|
2967
|
+
let versionResponse = await this.#makeRequest(peer, 'version');
|
|
2968
|
+
if (versionResponse instanceof Uint8Array) {
|
|
2969
|
+
versionResponse = new TextDecoder().decode(versionResponse);
|
|
2970
|
+
}
|
|
2971
|
+
if (typeof versionResponse === 'string') {
|
|
2972
|
+
peer.version = versionResponse;
|
|
2973
|
+
}
|
|
2974
|
+
else if (versionResponse &&
|
|
2975
|
+
typeof versionResponse === 'object' &&
|
|
2976
|
+
typeof versionResponse.version === 'string') {
|
|
2977
|
+
peer.version = versionResponse.version;
|
|
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
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
catch (error) {
|
|
2987
|
+
debug(`failed to request version from peer ${peerId}:`, error?.message ?? error);
|
|
2988
|
+
return;
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2967
2991
|
debug(`peer connected with version ${peer.version}`);
|
|
2968
2992
|
if (!this.isVersionCompatible(peer.version)) {
|
|
2969
|
-
|
|
2993
|
+
const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})`;
|
|
2994
|
+
console.error(`[chain] ${mismatchReason}`);
|
|
2995
|
+
await this.#recordPeerFailure(peerId, mismatchReason);
|
|
2970
2996
|
return;
|
|
2971
2997
|
}
|
|
2972
2998
|
let lastBlock;
|
|
@@ -2986,7 +3012,7 @@ class Chain extends VersionControl {
|
|
|
2986
3012
|
// This prevents Byzantine nodes from claiming a fake chain length to steer our sync
|
|
2987
3013
|
const localBlock = await this.lastBlock;
|
|
2988
3014
|
const MAX_SYNC_AHEAD = 100_000;
|
|
2989
|
-
if (lastBlock?.index > (localBlock?.index ?? 0) + MAX_SYNC_AHEAD) {
|
|
3015
|
+
if (lastBlock?.index > BigInt(localBlock?.index ?? 0) + BigInt(MAX_SYNC_AHEAD)) {
|
|
2990
3016
|
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || 'unknown';
|
|
2991
3017
|
debug(`Peer ${peerName} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`);
|
|
2992
3018
|
await this.#recordPeerFailure(peerId, `unreasonable lastBlock index: ${lastBlock.index}`);
|
|
@@ -3002,15 +3028,13 @@ class Chain extends VersionControl {
|
|
|
3002
3028
|
try {
|
|
3003
3029
|
let knownBlocksResponse = await this.#makeRequest(peer, 'knownBlocks');
|
|
3004
3030
|
if (knownBlocksResponse instanceof Uint8Array) {
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
console.log(e);
|
|
3010
|
-
}
|
|
3031
|
+
const reason = `knownBlocks must be object response, got raw bytes from ${peerId}`;
|
|
3032
|
+
debug(reason);
|
|
3033
|
+
await this.#recordPeerFailure(peerId, reason);
|
|
3034
|
+
return;
|
|
3011
3035
|
}
|
|
3012
3036
|
const MAX_WANTLIST_SIZE = 1000;
|
|
3013
|
-
if (knownBlocksResponse.blocks) {
|
|
3037
|
+
if (knownBlocksResponse && Array.isArray(knownBlocksResponse.blocks)) {
|
|
3014
3038
|
const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
|
|
3015
3039
|
if (remaining > 0) {
|
|
3016
3040
|
for (const hash of knownBlocksResponse.blocks.slice(0, remaining)) {
|
|
@@ -3052,12 +3076,7 @@ class Chain extends VersionControl {
|
|
|
3052
3076
|
try {
|
|
3053
3077
|
let stateInfo = await this.#makeRequest(peer, 'stateInfo');
|
|
3054
3078
|
if (stateInfo instanceof Uint8Array) {
|
|
3055
|
-
|
|
3056
|
-
stateInfo = JSON.parse(new TextDecoder().decode(stateInfo));
|
|
3057
|
-
}
|
|
3058
|
-
catch (e) {
|
|
3059
|
-
console.log(e);
|
|
3060
|
-
}
|
|
3079
|
+
stateInfo = new StateMessage(stateInfo).decoded;
|
|
3061
3080
|
}
|
|
3062
3081
|
await this.syncChain(lastBlock);
|
|
3063
3082
|
this.machine.states.info = stateInfo;
|
|
@@ -3075,7 +3094,7 @@ class Chain extends VersionControl {
|
|
|
3075
3094
|
return new globalThis.peernet.protos['peernet-response']({ response: pool });
|
|
3076
3095
|
}
|
|
3077
3096
|
async #versionHandler() {
|
|
3078
|
-
return new globalThis.peernet.protos['peernet-response']({ response:
|
|
3097
|
+
return new globalThis.peernet.protos['peernet-response']({ response: this.version });
|
|
3079
3098
|
}
|
|
3080
3099
|
async #executeTransaction({ hash, from, to, method, params, nonce }) {
|
|
3081
3100
|
try {
|
|
@@ -3427,12 +3446,14 @@ class Chain extends VersionControl {
|
|
|
3427
3446
|
debug(`created block: ${hash} @${block.index}`);
|
|
3428
3447
|
// Phase 2: announce proposal for consensus voting instead of direct add-block
|
|
3429
3448
|
console.log(`[consensus] 📤 Proposing block #${block.index} | hash: ${hash} | round: ${this.#consensusRound}`);
|
|
3430
|
-
const
|
|
3449
|
+
const proposalData = {
|
|
3431
3450
|
blockHash: hash,
|
|
3432
|
-
index: block.index,
|
|
3433
|
-
round: this.#consensusRound,
|
|
3451
|
+
index: BigInt(block.index),
|
|
3452
|
+
round: BigInt(this.#consensusRound),
|
|
3434
3453
|
from: peernet.selectedAccount
|
|
3435
|
-
}
|
|
3454
|
+
};
|
|
3455
|
+
const proposalMessage = new ProposalMessage(proposalData);
|
|
3456
|
+
const proposalPayload = proposalMessage.encoded;
|
|
3436
3457
|
try {
|
|
3437
3458
|
globalThis.peernet.publish('consensus:propose', proposalPayload);
|
|
3438
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
|
/**
|