@leofcoin/chain 1.9.11 → 1.9.12
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 +117 -79
- package/exports/chain.js +117 -79
- package/package.json +1 -1
package/exports/browser/chain.js
CHANGED
|
@@ -6116,22 +6116,22 @@ class Chain extends VersionControl {
|
|
|
6116
6116
|
#sleep(ms) {
|
|
6117
6117
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6118
6118
|
}
|
|
6119
|
-
async #recordPeerFailure(
|
|
6120
|
-
if (!this.#peerReputations.has(
|
|
6121
|
-
this.#peerReputations.set(
|
|
6119
|
+
async #recordPeerFailure(peerId2, reason) {
|
|
6120
|
+
if (!this.#peerReputations.has(peerId2)) {
|
|
6121
|
+
this.#peerReputations.set(peerId2, { score: 0, failures: [] });
|
|
6122
6122
|
}
|
|
6123
|
-
const rep = this.#peerReputations.get(
|
|
6123
|
+
const rep = this.#peerReputations.get(peerId2);
|
|
6124
6124
|
rep.score -= 1;
|
|
6125
6125
|
rep.failures.push(`${Date.now()}: ${reason}`);
|
|
6126
6126
|
if (rep.failures.length > this.#maxPeerFailures) {
|
|
6127
6127
|
rep.failures.shift();
|
|
6128
6128
|
}
|
|
6129
6129
|
if (rep.score < this.#minPeerScore) {
|
|
6130
|
-
console.warn(`[peer-ban] Peer ${
|
|
6130
|
+
console.warn(`[peer-ban] Peer ${peerId2} banned after ${rep.failures.length} failures`);
|
|
6131
6131
|
try {
|
|
6132
|
-
await globalThis.peernet.disconnect(
|
|
6132
|
+
await globalThis.peernet.disconnect(peerId2);
|
|
6133
6133
|
} catch (e) {
|
|
6134
|
-
debug(`Failed to disconnect peer ${
|
|
6134
|
+
debug(`Failed to disconnect peer ${peerId2}`);
|
|
6135
6135
|
}
|
|
6136
6136
|
}
|
|
6137
6137
|
}
|
|
@@ -6251,8 +6251,8 @@ class Chain extends VersionControl {
|
|
|
6251
6251
|
const start = Date.now();
|
|
6252
6252
|
try {
|
|
6253
6253
|
await this.#createBlock();
|
|
6254
|
-
} catch (
|
|
6255
|
-
console.error(
|
|
6254
|
+
} catch (error2) {
|
|
6255
|
+
console.error(error2);
|
|
6256
6256
|
}
|
|
6257
6257
|
const end = Date.now();
|
|
6258
6258
|
console.log((end - start) / 1e3 + " s");
|
|
@@ -6367,30 +6367,57 @@ class Chain extends VersionControl {
|
|
|
6367
6367
|
console.warn(`Deprecated: ${response.decoded.response} is not an Uint8Array`);
|
|
6368
6368
|
}
|
|
6369
6369
|
return response.decoded.response;
|
|
6370
|
-
} catch (
|
|
6371
|
-
const
|
|
6372
|
-
debug(`peernet request failed: ${request} -> ${
|
|
6373
|
-
throw
|
|
6370
|
+
} catch (error2) {
|
|
6371
|
+
const peerId2 = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
6372
|
+
debug(`peernet request failed: ${request} -> ${peerId2}:`, error2?.message ?? error2);
|
|
6373
|
+
throw error2;
|
|
6374
6374
|
}
|
|
6375
6375
|
}
|
|
6376
6376
|
async #decodeKnownBlocksResponse(response) {
|
|
6377
6377
|
if (!response) return null;
|
|
6378
|
+
if (Array.isArray(response)) {
|
|
6379
|
+
return { blocks: response };
|
|
6380
|
+
}
|
|
6378
6381
|
if (Array.isArray(response.blocks)) {
|
|
6379
6382
|
return { blocks: response.blocks };
|
|
6380
6383
|
}
|
|
6384
|
+
if (response.response && Array.isArray(response.response.blocks)) {
|
|
6385
|
+
return { blocks: response.response.blocks };
|
|
6386
|
+
}
|
|
6387
|
+
if (typeof response === "string") {
|
|
6388
|
+
try {
|
|
6389
|
+
const parsed = JSON.parse(response);
|
|
6390
|
+
if (Array.isArray(parsed)) return { blocks: parsed };
|
|
6391
|
+
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks };
|
|
6392
|
+
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks };
|
|
6393
|
+
} catch {
|
|
6394
|
+
return null;
|
|
6395
|
+
}
|
|
6396
|
+
return null;
|
|
6397
|
+
}
|
|
6381
6398
|
if (!(response instanceof Uint8Array)) return null;
|
|
6382
6399
|
let payload = response;
|
|
6383
|
-
for (let i = 0; i <
|
|
6400
|
+
for (let i = 0; i < 3; i++) {
|
|
6384
6401
|
if (!(payload instanceof Uint8Array)) break;
|
|
6385
6402
|
try {
|
|
6386
6403
|
const nestedResponse = await new globalThis.peernet.protos["peernet-response"](payload);
|
|
6387
6404
|
payload = nestedResponse?.decoded?.response;
|
|
6388
6405
|
} catch {
|
|
6389
|
-
|
|
6406
|
+
break;
|
|
6390
6407
|
}
|
|
6391
6408
|
}
|
|
6392
6409
|
if (Array.isArray(payload)) return { blocks: payload };
|
|
6393
6410
|
if (payload && Array.isArray(payload.blocks)) return { blocks: payload.blocks };
|
|
6411
|
+
if (payload?.response && Array.isArray(payload.response.blocks)) return { blocks: payload.response.blocks };
|
|
6412
|
+
try {
|
|
6413
|
+
const decoded = new TextDecoder().decode(response);
|
|
6414
|
+
const parsed = JSON.parse(decoded);
|
|
6415
|
+
if (Array.isArray(parsed)) return { blocks: parsed };
|
|
6416
|
+
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks };
|
|
6417
|
+
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks };
|
|
6418
|
+
} catch {
|
|
6419
|
+
return null;
|
|
6420
|
+
}
|
|
6394
6421
|
return null;
|
|
6395
6422
|
}
|
|
6396
6423
|
async getPeerTransactionPool(peer) {
|
|
@@ -6407,8 +6434,8 @@ class Chain extends VersionControl {
|
|
|
6407
6434
|
let txData;
|
|
6408
6435
|
try {
|
|
6409
6436
|
txData = await globalThis.peernet.get(key, "transaction");
|
|
6410
|
-
} catch (
|
|
6411
|
-
debug(`Failed to get transaction ${key}:`,
|
|
6437
|
+
} catch (error2) {
|
|
6438
|
+
debug(`Failed to get transaction ${key}:`, error2?.message ?? error2);
|
|
6412
6439
|
}
|
|
6413
6440
|
if (txData !== void 0) {
|
|
6414
6441
|
transactionsToGet.push(transactionPoolStore.put(key, txData));
|
|
@@ -6417,11 +6444,11 @@ class Chain extends VersionControl {
|
|
|
6417
6444
|
}
|
|
6418
6445
|
return Promise.all(transactionsToGet);
|
|
6419
6446
|
}
|
|
6420
|
-
async #peerConnected(
|
|
6421
|
-
debug(`peer connected: ${
|
|
6422
|
-
const peer = peernet.getConnection(
|
|
6447
|
+
async #peerConnected(peerId2) {
|
|
6448
|
+
debug(`peer connected: ${peerId2}`);
|
|
6449
|
+
const peer = peernet.getConnection(peerId2);
|
|
6423
6450
|
if (!peer) {
|
|
6424
|
-
debug(`peer not found: ${
|
|
6451
|
+
debug(`peer not found: ${peerId2}`);
|
|
6425
6452
|
return;
|
|
6426
6453
|
}
|
|
6427
6454
|
if (!peer.version) {
|
|
@@ -6436,13 +6463,13 @@ class Chain extends VersionControl {
|
|
|
6436
6463
|
peer.version = versionResponse.version;
|
|
6437
6464
|
}
|
|
6438
6465
|
if (!peer.version || typeof peer.version !== "string") {
|
|
6439
|
-
const reason = `invalid version response from peer ${
|
|
6466
|
+
const reason = `invalid version response from peer ${peerId2}`;
|
|
6440
6467
|
debug(reason);
|
|
6441
|
-
await this.#recordPeerFailure(
|
|
6468
|
+
await this.#recordPeerFailure(peerId2, reason);
|
|
6442
6469
|
return;
|
|
6443
6470
|
}
|
|
6444
|
-
} catch (
|
|
6445
|
-
debug(`failed to request version from peer ${
|
|
6471
|
+
} catch (error2) {
|
|
6472
|
+
debug(`failed to request version from peer ${peerId2}:`, error2?.message ?? error2);
|
|
6446
6473
|
return;
|
|
6447
6474
|
}
|
|
6448
6475
|
}
|
|
@@ -6450,31 +6477,32 @@ class Chain extends VersionControl {
|
|
|
6450
6477
|
if (!this.isVersionCompatible(peer.version)) {
|
|
6451
6478
|
const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})`;
|
|
6452
6479
|
console.error(`[chain] ${mismatchReason}`);
|
|
6453
|
-
await this.#recordPeerFailure(
|
|
6480
|
+
await this.#recordPeerFailure(peerId2, mismatchReason);
|
|
6454
6481
|
return;
|
|
6455
6482
|
}
|
|
6456
6483
|
let lastBlock;
|
|
6457
6484
|
try {
|
|
6458
6485
|
console.log("requesting last block from peer...");
|
|
6459
6486
|
console.log(await this.lastBlock);
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6487
|
+
const lastBlockRaw = await this.#makeRequest(peer, "lastBlock");
|
|
6488
|
+
console.log(new LastBlockMessage(lastBlockRaw));
|
|
6489
|
+
lastBlock = new LastBlockMessage(lastBlockRaw).decoded;
|
|
6490
|
+
} catch (error2) {
|
|
6491
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
6492
|
+
debug(`lastBlock request failed: ${peerName2}:`, error2?.message ?? error2);
|
|
6493
|
+
await this.#recordPeerFailure(peerId2, `lastBlock request failed: ${error2?.message ?? error2}`);
|
|
6466
6494
|
return;
|
|
6467
6495
|
}
|
|
6468
6496
|
const localBlock = await this.lastBlock;
|
|
6469
6497
|
const MAX_SYNC_AHEAD = 1e5;
|
|
6470
6498
|
if (lastBlock?.index > BigInt(localBlock?.index ?? 0) + BigInt(MAX_SYNC_AHEAD)) {
|
|
6471
|
-
const
|
|
6472
|
-
debug(`Peer ${
|
|
6473
|
-
await this.#recordPeerFailure(
|
|
6499
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
6500
|
+
debug(`Peer ${peerName2} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`);
|
|
6501
|
+
await this.#recordPeerFailure(peerId2, `unreasonable lastBlock index: ${lastBlock.index}`);
|
|
6474
6502
|
return;
|
|
6475
6503
|
}
|
|
6476
6504
|
if (!lastBlock || !lastBlock.hash || lastBlock.hash === "0x0") {
|
|
6477
|
-
debug(`peer has no lastBlock: ${
|
|
6505
|
+
debug(`peer has no lastBlock: ${peerId2}`);
|
|
6478
6506
|
return;
|
|
6479
6507
|
}
|
|
6480
6508
|
const higherThenCurrentLocal = !localBlock?.index ? true : lastBlock.index > localBlock.index;
|
|
@@ -6484,23 +6512,24 @@ class Chain extends VersionControl {
|
|
|
6484
6512
|
const knownBlocksRaw = await this.#makeRequest(peer, "knownBlocks");
|
|
6485
6513
|
const knownBlocksResponse = await this.#decodeKnownBlocksResponse(knownBlocksRaw);
|
|
6486
6514
|
if (!knownBlocksResponse) {
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6515
|
+
debug(
|
|
6516
|
+
`knownBlocks decode failed for peer ${peerId2} (non-fatal), continuing sync without prefilled wantList`
|
|
6517
|
+
);
|
|
6518
|
+
} else {
|
|
6519
|
+
const MAX_WANTLIST_SIZE = 1e3;
|
|
6520
|
+
const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
|
|
6521
|
+
if (remaining > 0) {
|
|
6522
|
+
for (const hash of knownBlocksResponse.blocks.slice(0, remaining)) {
|
|
6523
|
+
this.wantList.push(hash);
|
|
6524
|
+
}
|
|
6497
6525
|
}
|
|
6498
6526
|
}
|
|
6499
|
-
} catch (
|
|
6500
|
-
const
|
|
6501
|
-
debug(
|
|
6502
|
-
|
|
6503
|
-
|
|
6527
|
+
} catch (error2) {
|
|
6528
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
6529
|
+
debug(
|
|
6530
|
+
`knownBlocks request failed: ${peerName2} (non-fatal), continuing sync without prefilled wantList:`,
|
|
6531
|
+
error2?.message ?? error2
|
|
6532
|
+
);
|
|
6504
6533
|
}
|
|
6505
6534
|
}
|
|
6506
6535
|
}
|
|
@@ -6516,9 +6545,9 @@ class Chain extends VersionControl {
|
|
|
6516
6545
|
try {
|
|
6517
6546
|
const peerTransactionPool = higherThenCurrentLocal && await this.getPeerTransactionPool(peer) || [];
|
|
6518
6547
|
if (this.#participating && peerTransactionPool.length > 0) return this.#runEpoch();
|
|
6519
|
-
} catch (
|
|
6520
|
-
const
|
|
6521
|
-
debug(`transactionPool request failed: ${
|
|
6548
|
+
} catch (error2) {
|
|
6549
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
6550
|
+
debug(`transactionPool request failed: ${peerName2}:`, error2?.message ?? error2);
|
|
6522
6551
|
}
|
|
6523
6552
|
}, 3e3);
|
|
6524
6553
|
try {
|
|
@@ -6526,12 +6555,18 @@ class Chain extends VersionControl {
|
|
|
6526
6555
|
if (stateInfo instanceof Uint8Array) {
|
|
6527
6556
|
stateInfo = new StateMessage(stateInfo).decoded;
|
|
6528
6557
|
}
|
|
6558
|
+
debug(
|
|
6559
|
+
`sync start with peer ${peerId2}: local=${localBlock?.index ?? -1} remote=${lastBlock?.index ?? -1} hash=${lastBlock?.hash}`
|
|
6560
|
+
);
|
|
6529
6561
|
await this.syncChain(lastBlock);
|
|
6562
|
+
debug(
|
|
6563
|
+
`sync finished with peer ${peerId2}: state=${this.syncState} localNow=${(await this.lastBlock)?.index ?? -1}`
|
|
6564
|
+
);
|
|
6530
6565
|
this.machine.states.info = stateInfo;
|
|
6531
|
-
} catch (
|
|
6532
|
-
const
|
|
6533
|
-
debug(`stateInfo/syncChain failed: ${
|
|
6534
|
-
await this.#recordPeerFailure(
|
|
6566
|
+
} catch (error2) {
|
|
6567
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
6568
|
+
debug(`stateInfo/syncChain failed: ${peerName2}:`, error2?.message ?? error2);
|
|
6569
|
+
await this.#recordPeerFailure(peerId2, `stateInfo/syncChain failed: ${error2?.message ?? error2}`);
|
|
6535
6570
|
return;
|
|
6536
6571
|
}
|
|
6537
6572
|
}
|
|
@@ -6547,16 +6582,19 @@ class Chain extends VersionControl {
|
|
|
6547
6582
|
try {
|
|
6548
6583
|
let result = await this.machine.execute(to, method, params);
|
|
6549
6584
|
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fulfilled", hash });
|
|
6550
|
-
|
|
6551
|
-
|
|
6585
|
+
debug(
|
|
6586
|
+
`lastBlock request failed: ${peerName} (non-fatal), continuing sync without prefilled wantList:`,
|
|
6587
|
+
error?.message ?? error
|
|
6588
|
+
);
|
|
6589
|
+
} catch (error2) {
|
|
6552
6590
|
await transactionPoolStore.delete(hash);
|
|
6553
6591
|
try {
|
|
6554
6592
|
globalThis.peernet.publish("invalid-transaction", hash);
|
|
6555
6593
|
} catch (publishError) {
|
|
6556
6594
|
debug("peernet publish failed: invalid-transaction", publishError?.message ?? publishError);
|
|
6557
6595
|
}
|
|
6558
|
-
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fail", hash, error });
|
|
6559
|
-
throw { error, hash, from, to, params, nonce };
|
|
6596
|
+
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fail", hash, error: error2 });
|
|
6597
|
+
throw { error: error2, hash, from, to, params, nonce };
|
|
6560
6598
|
}
|
|
6561
6599
|
}
|
|
6562
6600
|
async #addBlock(block) {
|
|
@@ -6567,7 +6605,7 @@ class Chain extends VersionControl {
|
|
|
6567
6605
|
const existingBlockAtHeight = this.#blocks[blockIndex];
|
|
6568
6606
|
if (existingBlockAtHeight) {
|
|
6569
6607
|
if (existingBlockAtHeight.hash !== hash) {
|
|
6570
|
-
|
|
6608
|
+
debug(`knownBlocks decode failed for peer ${peerId} (non-fatal), continuing sync without prefilled wantList`);
|
|
6571
6609
|
console.error(` Local: ${existingBlockAtHeight.hash}`);
|
|
6572
6610
|
console.error(` Remote: ${hash}`);
|
|
6573
6611
|
throw new Error(`Block conflict detected at index ${blockIndex}`);
|
|
@@ -6675,10 +6713,10 @@ class Chain extends VersionControl {
|
|
|
6675
6713
|
await this.updateState(blockMessage);
|
|
6676
6714
|
}
|
|
6677
6715
|
globalThis.pubsub.publish("block-processed", blockMessage.decoded);
|
|
6678
|
-
} catch (
|
|
6679
|
-
console.log(
|
|
6716
|
+
} catch (error2) {
|
|
6717
|
+
console.log(error2.hash);
|
|
6680
6718
|
console.log("errrrr");
|
|
6681
|
-
await transactionPoolStore.delete(
|
|
6719
|
+
await transactionPoolStore.delete(error2.hash);
|
|
6682
6720
|
}
|
|
6683
6721
|
}
|
|
6684
6722
|
async participate(address) {
|
|
@@ -6696,12 +6734,12 @@ class Chain extends VersionControl {
|
|
|
6696
6734
|
const transaction = await signTransaction(rawTransaction, globalThis.peernet.identity);
|
|
6697
6735
|
try {
|
|
6698
6736
|
await this.sendTransaction(transaction);
|
|
6699
|
-
} catch (
|
|
6700
|
-
console.error(
|
|
6737
|
+
} catch (error2) {
|
|
6738
|
+
console.error(error2);
|
|
6701
6739
|
}
|
|
6702
6740
|
}
|
|
6703
|
-
} catch (
|
|
6704
|
-
debug("Error in participate:",
|
|
6741
|
+
} catch (error2) {
|
|
6742
|
+
debug("Error in participate:", error2.message);
|
|
6705
6743
|
}
|
|
6706
6744
|
if (await this.hasTransactionToHandle() && !this.#runningEpoch && this.#participating) await this.#runEpoch();
|
|
6707
6745
|
}
|
|
@@ -6798,9 +6836,9 @@ class Chain extends VersionControl {
|
|
|
6798
6836
|
address: validator,
|
|
6799
6837
|
bw: bw.up + bw.down
|
|
6800
6838
|
};
|
|
6801
|
-
} catch (
|
|
6802
|
-
const
|
|
6803
|
-
debug(`bw request failed: ${
|
|
6839
|
+
} catch (error2) {
|
|
6840
|
+
const peerId2 = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
6841
|
+
debug(`bw request failed: ${peerId2}:`, error2?.message ?? error2);
|
|
6804
6842
|
return null;
|
|
6805
6843
|
}
|
|
6806
6844
|
} else if (globalThis.peernet.selectedAccount === validator) {
|
|
@@ -6818,8 +6856,8 @@ class Chain extends VersionControl {
|
|
|
6818
6856
|
}
|
|
6819
6857
|
}
|
|
6820
6858
|
};
|
|
6821
|
-
finalizeBWAndBroadcast().catch((
|
|
6822
|
-
debug(`background BW finalization failed:`,
|
|
6859
|
+
finalizeBWAndBroadcast().catch((error2) => {
|
|
6860
|
+
debug(`background BW finalization failed:`, error2?.message ?? error2);
|
|
6823
6861
|
});
|
|
6824
6862
|
block.validators = block.validators.map((validator) => {
|
|
6825
6863
|
validator.reward = block.fees;
|
|
@@ -6884,8 +6922,8 @@ class Chain extends VersionControl {
|
|
|
6884
6922
|
debug("peernet publish failed: consensus:propose", publishError?.message ?? publishError);
|
|
6885
6923
|
}
|
|
6886
6924
|
await this.#castVote("prevote", hash, block.index, this.#consensusRound);
|
|
6887
|
-
} catch (
|
|
6888
|
-
console.log(
|
|
6925
|
+
} catch (error2) {
|
|
6926
|
+
console.log(error2);
|
|
6889
6927
|
throw new Error(`invalid block ${block}`);
|
|
6890
6928
|
}
|
|
6891
6929
|
}
|
|
@@ -7016,8 +7054,8 @@ class Chain extends VersionControl {
|
|
|
7016
7054
|
if (globalThis.peernet && globalThis.peernet.start) {
|
|
7017
7055
|
await globalThis.peernet.start();
|
|
7018
7056
|
}
|
|
7019
|
-
} catch (
|
|
7020
|
-
console.warn("Failed to reconnect to peers:",
|
|
7057
|
+
} catch (error2) {
|
|
7058
|
+
console.warn("Failed to reconnect to peers:", error2.message);
|
|
7021
7059
|
}
|
|
7022
7060
|
}
|
|
7023
7061
|
}
|
package/exports/chain.js
CHANGED
|
@@ -2339,22 +2339,22 @@ class Chain extends VersionControl {
|
|
|
2339
2339
|
#sleep(ms) {
|
|
2340
2340
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2341
2341
|
}
|
|
2342
|
-
async #recordPeerFailure(
|
|
2343
|
-
if (!this.#peerReputations.has(
|
|
2344
|
-
this.#peerReputations.set(
|
|
2342
|
+
async #recordPeerFailure(peerId2, reason) {
|
|
2343
|
+
if (!this.#peerReputations.has(peerId2)) {
|
|
2344
|
+
this.#peerReputations.set(peerId2, { score: 0, failures: [] });
|
|
2345
2345
|
}
|
|
2346
|
-
const rep = this.#peerReputations.get(
|
|
2346
|
+
const rep = this.#peerReputations.get(peerId2);
|
|
2347
2347
|
rep.score -= 1;
|
|
2348
2348
|
rep.failures.push(`${Date.now()}: ${reason}`);
|
|
2349
2349
|
if (rep.failures.length > this.#maxPeerFailures) {
|
|
2350
2350
|
rep.failures.shift();
|
|
2351
2351
|
}
|
|
2352
2352
|
if (rep.score < this.#minPeerScore) {
|
|
2353
|
-
console.warn(`[peer-ban] Peer ${
|
|
2353
|
+
console.warn(`[peer-ban] Peer ${peerId2} banned after ${rep.failures.length} failures`);
|
|
2354
2354
|
try {
|
|
2355
|
-
await globalThis.peernet.disconnect(
|
|
2355
|
+
await globalThis.peernet.disconnect(peerId2);
|
|
2356
2356
|
} catch (e) {
|
|
2357
|
-
debug(`Failed to disconnect peer ${
|
|
2357
|
+
debug(`Failed to disconnect peer ${peerId2}`);
|
|
2358
2358
|
}
|
|
2359
2359
|
}
|
|
2360
2360
|
}
|
|
@@ -2474,8 +2474,8 @@ class Chain extends VersionControl {
|
|
|
2474
2474
|
const start = Date.now();
|
|
2475
2475
|
try {
|
|
2476
2476
|
await this.#createBlock();
|
|
2477
|
-
} catch (
|
|
2478
|
-
console.error(
|
|
2477
|
+
} catch (error2) {
|
|
2478
|
+
console.error(error2);
|
|
2479
2479
|
}
|
|
2480
2480
|
const end = Date.now();
|
|
2481
2481
|
console.log((end - start) / 1e3 + " s");
|
|
@@ -2590,30 +2590,57 @@ class Chain extends VersionControl {
|
|
|
2590
2590
|
console.warn(`Deprecated: ${response.decoded.response} is not an Uint8Array`);
|
|
2591
2591
|
}
|
|
2592
2592
|
return response.decoded.response;
|
|
2593
|
-
} catch (
|
|
2594
|
-
const
|
|
2595
|
-
debug(`peernet request failed: ${request} -> ${
|
|
2596
|
-
throw
|
|
2593
|
+
} catch (error2) {
|
|
2594
|
+
const peerId2 = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
2595
|
+
debug(`peernet request failed: ${request} -> ${peerId2}:`, error2?.message ?? error2);
|
|
2596
|
+
throw error2;
|
|
2597
2597
|
}
|
|
2598
2598
|
}
|
|
2599
2599
|
async #decodeKnownBlocksResponse(response) {
|
|
2600
2600
|
if (!response) return null;
|
|
2601
|
+
if (Array.isArray(response)) {
|
|
2602
|
+
return { blocks: response };
|
|
2603
|
+
}
|
|
2601
2604
|
if (Array.isArray(response.blocks)) {
|
|
2602
2605
|
return { blocks: response.blocks };
|
|
2603
2606
|
}
|
|
2607
|
+
if (response.response && Array.isArray(response.response.blocks)) {
|
|
2608
|
+
return { blocks: response.response.blocks };
|
|
2609
|
+
}
|
|
2610
|
+
if (typeof response === "string") {
|
|
2611
|
+
try {
|
|
2612
|
+
const parsed = JSON.parse(response);
|
|
2613
|
+
if (Array.isArray(parsed)) return { blocks: parsed };
|
|
2614
|
+
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks };
|
|
2615
|
+
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks };
|
|
2616
|
+
} catch {
|
|
2617
|
+
return null;
|
|
2618
|
+
}
|
|
2619
|
+
return null;
|
|
2620
|
+
}
|
|
2604
2621
|
if (!(response instanceof Uint8Array)) return null;
|
|
2605
2622
|
let payload = response;
|
|
2606
|
-
for (let i = 0; i <
|
|
2623
|
+
for (let i = 0; i < 3; i++) {
|
|
2607
2624
|
if (!(payload instanceof Uint8Array)) break;
|
|
2608
2625
|
try {
|
|
2609
2626
|
const nestedResponse = await new globalThis.peernet.protos["peernet-response"](payload);
|
|
2610
2627
|
payload = nestedResponse?.decoded?.response;
|
|
2611
2628
|
} catch {
|
|
2612
|
-
|
|
2629
|
+
break;
|
|
2613
2630
|
}
|
|
2614
2631
|
}
|
|
2615
2632
|
if (Array.isArray(payload)) return { blocks: payload };
|
|
2616
2633
|
if (payload && Array.isArray(payload.blocks)) return { blocks: payload.blocks };
|
|
2634
|
+
if (payload?.response && Array.isArray(payload.response.blocks)) return { blocks: payload.response.blocks };
|
|
2635
|
+
try {
|
|
2636
|
+
const decoded = new TextDecoder().decode(response);
|
|
2637
|
+
const parsed = JSON.parse(decoded);
|
|
2638
|
+
if (Array.isArray(parsed)) return { blocks: parsed };
|
|
2639
|
+
if (parsed && Array.isArray(parsed.blocks)) return { blocks: parsed.blocks };
|
|
2640
|
+
if (parsed?.response && Array.isArray(parsed.response.blocks)) return { blocks: parsed.response.blocks };
|
|
2641
|
+
} catch {
|
|
2642
|
+
return null;
|
|
2643
|
+
}
|
|
2617
2644
|
return null;
|
|
2618
2645
|
}
|
|
2619
2646
|
async getPeerTransactionPool(peer) {
|
|
@@ -2630,8 +2657,8 @@ class Chain extends VersionControl {
|
|
|
2630
2657
|
let txData;
|
|
2631
2658
|
try {
|
|
2632
2659
|
txData = await globalThis.peernet.get(key, "transaction");
|
|
2633
|
-
} catch (
|
|
2634
|
-
debug(`Failed to get transaction ${key}:`,
|
|
2660
|
+
} catch (error2) {
|
|
2661
|
+
debug(`Failed to get transaction ${key}:`, error2?.message ?? error2);
|
|
2635
2662
|
}
|
|
2636
2663
|
if (txData !== void 0) {
|
|
2637
2664
|
transactionsToGet.push(transactionPoolStore.put(key, txData));
|
|
@@ -2640,11 +2667,11 @@ class Chain extends VersionControl {
|
|
|
2640
2667
|
}
|
|
2641
2668
|
return Promise.all(transactionsToGet);
|
|
2642
2669
|
}
|
|
2643
|
-
async #peerConnected(
|
|
2644
|
-
debug(`peer connected: ${
|
|
2645
|
-
const peer = peernet.getConnection(
|
|
2670
|
+
async #peerConnected(peerId2) {
|
|
2671
|
+
debug(`peer connected: ${peerId2}`);
|
|
2672
|
+
const peer = peernet.getConnection(peerId2);
|
|
2646
2673
|
if (!peer) {
|
|
2647
|
-
debug(`peer not found: ${
|
|
2674
|
+
debug(`peer not found: ${peerId2}`);
|
|
2648
2675
|
return;
|
|
2649
2676
|
}
|
|
2650
2677
|
if (!peer.version) {
|
|
@@ -2659,13 +2686,13 @@ class Chain extends VersionControl {
|
|
|
2659
2686
|
peer.version = versionResponse.version;
|
|
2660
2687
|
}
|
|
2661
2688
|
if (!peer.version || typeof peer.version !== "string") {
|
|
2662
|
-
const reason = `invalid version response from peer ${
|
|
2689
|
+
const reason = `invalid version response from peer ${peerId2}`;
|
|
2663
2690
|
debug(reason);
|
|
2664
|
-
await this.#recordPeerFailure(
|
|
2691
|
+
await this.#recordPeerFailure(peerId2, reason);
|
|
2665
2692
|
return;
|
|
2666
2693
|
}
|
|
2667
|
-
} catch (
|
|
2668
|
-
debug(`failed to request version from peer ${
|
|
2694
|
+
} catch (error2) {
|
|
2695
|
+
debug(`failed to request version from peer ${peerId2}:`, error2?.message ?? error2);
|
|
2669
2696
|
return;
|
|
2670
2697
|
}
|
|
2671
2698
|
}
|
|
@@ -2673,31 +2700,32 @@ class Chain extends VersionControl {
|
|
|
2673
2700
|
if (!this.isVersionCompatible(peer.version)) {
|
|
2674
2701
|
const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})`;
|
|
2675
2702
|
console.error(`[chain] ${mismatchReason}`);
|
|
2676
|
-
await this.#recordPeerFailure(
|
|
2703
|
+
await this.#recordPeerFailure(peerId2, mismatchReason);
|
|
2677
2704
|
return;
|
|
2678
2705
|
}
|
|
2679
2706
|
let lastBlock;
|
|
2680
2707
|
try {
|
|
2681
2708
|
console.log("requesting last block from peer...");
|
|
2682
2709
|
console.log(await this.lastBlock);
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2710
|
+
const lastBlockRaw = await this.#makeRequest(peer, "lastBlock");
|
|
2711
|
+
console.log(new LastBlockMessage(lastBlockRaw));
|
|
2712
|
+
lastBlock = new LastBlockMessage(lastBlockRaw).decoded;
|
|
2713
|
+
} catch (error2) {
|
|
2714
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
2715
|
+
debug(`lastBlock request failed: ${peerName2}:`, error2?.message ?? error2);
|
|
2716
|
+
await this.#recordPeerFailure(peerId2, `lastBlock request failed: ${error2?.message ?? error2}`);
|
|
2689
2717
|
return;
|
|
2690
2718
|
}
|
|
2691
2719
|
const localBlock = await this.lastBlock;
|
|
2692
2720
|
const MAX_SYNC_AHEAD = 1e5;
|
|
2693
2721
|
if (lastBlock?.index > BigInt(localBlock?.index ?? 0) + BigInt(MAX_SYNC_AHEAD)) {
|
|
2694
|
-
const
|
|
2695
|
-
debug(`Peer ${
|
|
2696
|
-
await this.#recordPeerFailure(
|
|
2722
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
2723
|
+
debug(`Peer ${peerName2} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`);
|
|
2724
|
+
await this.#recordPeerFailure(peerId2, `unreasonable lastBlock index: ${lastBlock.index}`);
|
|
2697
2725
|
return;
|
|
2698
2726
|
}
|
|
2699
2727
|
if (!lastBlock || !lastBlock.hash || lastBlock.hash === "0x0") {
|
|
2700
|
-
debug(`peer has no lastBlock: ${
|
|
2728
|
+
debug(`peer has no lastBlock: ${peerId2}`);
|
|
2701
2729
|
return;
|
|
2702
2730
|
}
|
|
2703
2731
|
const higherThenCurrentLocal = !localBlock?.index ? true : lastBlock.index > localBlock.index;
|
|
@@ -2707,23 +2735,24 @@ class Chain extends VersionControl {
|
|
|
2707
2735
|
const knownBlocksRaw = await this.#makeRequest(peer, "knownBlocks");
|
|
2708
2736
|
const knownBlocksResponse = await this.#decodeKnownBlocksResponse(knownBlocksRaw);
|
|
2709
2737
|
if (!knownBlocksResponse) {
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2738
|
+
debug(
|
|
2739
|
+
`knownBlocks decode failed for peer ${peerId2} (non-fatal), continuing sync without prefilled wantList`
|
|
2740
|
+
);
|
|
2741
|
+
} else {
|
|
2742
|
+
const MAX_WANTLIST_SIZE = 1e3;
|
|
2743
|
+
const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
|
|
2744
|
+
if (remaining > 0) {
|
|
2745
|
+
for (const hash of knownBlocksResponse.blocks.slice(0, remaining)) {
|
|
2746
|
+
this.wantList.push(hash);
|
|
2747
|
+
}
|
|
2720
2748
|
}
|
|
2721
2749
|
}
|
|
2722
|
-
} catch (
|
|
2723
|
-
const
|
|
2724
|
-
debug(
|
|
2725
|
-
|
|
2726
|
-
|
|
2750
|
+
} catch (error2) {
|
|
2751
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
2752
|
+
debug(
|
|
2753
|
+
`knownBlocks request failed: ${peerName2} (non-fatal), continuing sync without prefilled wantList:`,
|
|
2754
|
+
error2?.message ?? error2
|
|
2755
|
+
);
|
|
2727
2756
|
}
|
|
2728
2757
|
}
|
|
2729
2758
|
}
|
|
@@ -2739,9 +2768,9 @@ class Chain extends VersionControl {
|
|
|
2739
2768
|
try {
|
|
2740
2769
|
const peerTransactionPool = higherThenCurrentLocal && await this.getPeerTransactionPool(peer) || [];
|
|
2741
2770
|
if (this.#participating && peerTransactionPool.length > 0) return this.#runEpoch();
|
|
2742
|
-
} catch (
|
|
2743
|
-
const
|
|
2744
|
-
debug(`transactionPool request failed: ${
|
|
2771
|
+
} catch (error2) {
|
|
2772
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
2773
|
+
debug(`transactionPool request failed: ${peerName2}:`, error2?.message ?? error2);
|
|
2745
2774
|
}
|
|
2746
2775
|
}, 3e3);
|
|
2747
2776
|
try {
|
|
@@ -2749,12 +2778,18 @@ class Chain extends VersionControl {
|
|
|
2749
2778
|
if (stateInfo instanceof Uint8Array) {
|
|
2750
2779
|
stateInfo = new StateMessage(stateInfo).decoded;
|
|
2751
2780
|
}
|
|
2781
|
+
debug(
|
|
2782
|
+
`sync start with peer ${peerId2}: local=${localBlock?.index ?? -1} remote=${lastBlock?.index ?? -1} hash=${lastBlock?.hash}`
|
|
2783
|
+
);
|
|
2752
2784
|
await this.syncChain(lastBlock);
|
|
2785
|
+
debug(
|
|
2786
|
+
`sync finished with peer ${peerId2}: state=${this.syncState} localNow=${(await this.lastBlock)?.index ?? -1}`
|
|
2787
|
+
);
|
|
2753
2788
|
this.machine.states.info = stateInfo;
|
|
2754
|
-
} catch (
|
|
2755
|
-
const
|
|
2756
|
-
debug(`stateInfo/syncChain failed: ${
|
|
2757
|
-
await this.#recordPeerFailure(
|
|
2789
|
+
} catch (error2) {
|
|
2790
|
+
const peerName2 = peer?.peerId || peer?.id || peer?.address || peerId2 || "unknown";
|
|
2791
|
+
debug(`stateInfo/syncChain failed: ${peerName2}:`, error2?.message ?? error2);
|
|
2792
|
+
await this.#recordPeerFailure(peerId2, `stateInfo/syncChain failed: ${error2?.message ?? error2}`);
|
|
2758
2793
|
return;
|
|
2759
2794
|
}
|
|
2760
2795
|
}
|
|
@@ -2770,16 +2805,19 @@ class Chain extends VersionControl {
|
|
|
2770
2805
|
try {
|
|
2771
2806
|
let result = await this.machine.execute(to, method, params);
|
|
2772
2807
|
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fulfilled", hash });
|
|
2773
|
-
|
|
2774
|
-
|
|
2808
|
+
debug(
|
|
2809
|
+
`lastBlock request failed: ${peerName} (non-fatal), continuing sync without prefilled wantList:`,
|
|
2810
|
+
error?.message ?? error
|
|
2811
|
+
);
|
|
2812
|
+
} catch (error2) {
|
|
2775
2813
|
await transactionPoolStore.delete(hash);
|
|
2776
2814
|
try {
|
|
2777
2815
|
globalThis.peernet.publish("invalid-transaction", hash);
|
|
2778
2816
|
} catch (publishError) {
|
|
2779
2817
|
debug("peernet publish failed: invalid-transaction", publishError?.message ?? publishError);
|
|
2780
2818
|
}
|
|
2781
|
-
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fail", hash, error });
|
|
2782
|
-
throw { error, hash, from, to, params, nonce };
|
|
2819
|
+
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fail", hash, error: error2 });
|
|
2820
|
+
throw { error: error2, hash, from, to, params, nonce };
|
|
2783
2821
|
}
|
|
2784
2822
|
}
|
|
2785
2823
|
async #addBlock(block) {
|
|
@@ -2790,7 +2828,7 @@ class Chain extends VersionControl {
|
|
|
2790
2828
|
const existingBlockAtHeight = this.#blocks[blockIndex];
|
|
2791
2829
|
if (existingBlockAtHeight) {
|
|
2792
2830
|
if (existingBlockAtHeight.hash !== hash) {
|
|
2793
|
-
|
|
2831
|
+
debug(`knownBlocks decode failed for peer ${peerId} (non-fatal), continuing sync without prefilled wantList`);
|
|
2794
2832
|
console.error(` Local: ${existingBlockAtHeight.hash}`);
|
|
2795
2833
|
console.error(` Remote: ${hash}`);
|
|
2796
2834
|
throw new Error(`Block conflict detected at index ${blockIndex}`);
|
|
@@ -2898,10 +2936,10 @@ class Chain extends VersionControl {
|
|
|
2898
2936
|
await this.updateState(blockMessage);
|
|
2899
2937
|
}
|
|
2900
2938
|
globalThis.pubsub.publish("block-processed", blockMessage.decoded);
|
|
2901
|
-
} catch (
|
|
2902
|
-
console.log(
|
|
2939
|
+
} catch (error2) {
|
|
2940
|
+
console.log(error2.hash);
|
|
2903
2941
|
console.log("errrrr");
|
|
2904
|
-
await transactionPoolStore.delete(
|
|
2942
|
+
await transactionPoolStore.delete(error2.hash);
|
|
2905
2943
|
}
|
|
2906
2944
|
}
|
|
2907
2945
|
async participate(address) {
|
|
@@ -2919,12 +2957,12 @@ class Chain extends VersionControl {
|
|
|
2919
2957
|
const transaction = await signTransaction(rawTransaction, globalThis.peernet.identity);
|
|
2920
2958
|
try {
|
|
2921
2959
|
await this.sendTransaction(transaction);
|
|
2922
|
-
} catch (
|
|
2923
|
-
console.error(
|
|
2960
|
+
} catch (error2) {
|
|
2961
|
+
console.error(error2);
|
|
2924
2962
|
}
|
|
2925
2963
|
}
|
|
2926
|
-
} catch (
|
|
2927
|
-
debug("Error in participate:",
|
|
2964
|
+
} catch (error2) {
|
|
2965
|
+
debug("Error in participate:", error2.message);
|
|
2928
2966
|
}
|
|
2929
2967
|
if (await this.hasTransactionToHandle() && !this.#runningEpoch && this.#participating) await this.#runEpoch();
|
|
2930
2968
|
}
|
|
@@ -3021,9 +3059,9 @@ class Chain extends VersionControl {
|
|
|
3021
3059
|
address: validator,
|
|
3022
3060
|
bw: bw.up + bw.down
|
|
3023
3061
|
};
|
|
3024
|
-
} catch (
|
|
3025
|
-
const
|
|
3026
|
-
debug(`bw request failed: ${
|
|
3062
|
+
} catch (error2) {
|
|
3063
|
+
const peerId2 = peer?.peerId || peer?.id || peer?.address || "unknown";
|
|
3064
|
+
debug(`bw request failed: ${peerId2}:`, error2?.message ?? error2);
|
|
3027
3065
|
return null;
|
|
3028
3066
|
}
|
|
3029
3067
|
} else if (globalThis.peernet.selectedAccount === validator) {
|
|
@@ -3041,8 +3079,8 @@ class Chain extends VersionControl {
|
|
|
3041
3079
|
}
|
|
3042
3080
|
}
|
|
3043
3081
|
};
|
|
3044
|
-
finalizeBWAndBroadcast().catch((
|
|
3045
|
-
debug(`background BW finalization failed:`,
|
|
3082
|
+
finalizeBWAndBroadcast().catch((error2) => {
|
|
3083
|
+
debug(`background BW finalization failed:`, error2?.message ?? error2);
|
|
3046
3084
|
});
|
|
3047
3085
|
block.validators = block.validators.map((validator) => {
|
|
3048
3086
|
validator.reward = block.fees;
|
|
@@ -3107,8 +3145,8 @@ class Chain extends VersionControl {
|
|
|
3107
3145
|
debug("peernet publish failed: consensus:propose", publishError?.message ?? publishError);
|
|
3108
3146
|
}
|
|
3109
3147
|
await this.#castVote("prevote", hash, block.index, this.#consensusRound);
|
|
3110
|
-
} catch (
|
|
3111
|
-
console.log(
|
|
3148
|
+
} catch (error2) {
|
|
3149
|
+
console.log(error2);
|
|
3112
3150
|
throw new Error(`invalid block ${block}`);
|
|
3113
3151
|
}
|
|
3114
3152
|
}
|
|
@@ -3239,8 +3277,8 @@ class Chain extends VersionControl {
|
|
|
3239
3277
|
if (globalThis.peernet && globalThis.peernet.start) {
|
|
3240
3278
|
await globalThis.peernet.start();
|
|
3241
3279
|
}
|
|
3242
|
-
} catch (
|
|
3243
|
-
console.warn("Failed to reconnect to peers:",
|
|
3280
|
+
} catch (error2) {
|
|
3281
|
+
console.warn("Failed to reconnect to peers:", error2.message);
|
|
3244
3282
|
}
|
|
3245
3283
|
}
|
|
3246
3284
|
}
|