@leofcoin/chain 1.7.72 → 1.7.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 +172 -6
- package/exports/browser/{client-DD7vhDK_-UAymLS2i.js → client-DD7vhDK_-BdNAdRzV.js} +1 -1
- package/exports/browser/{messages-CW17jRdc-c9cSXiiQ.js → messages-CW17jRdc-Dtrj-vba.js} +1 -1
- package/exports/browser/{node-browser-BvxzAdLe.js → node-browser-DMmrhyQS.js} +3 -3
- package/exports/browser/node-browser.js +1 -1
- package/exports/chain.js +172 -6
- package/exports/connection-monitor.d.ts +14 -0
- package/exports/sync-controller.d.ts +3 -2
- package/package.json +2 -2
package/exports/browser/chain.js
CHANGED
|
@@ -4219,7 +4219,10 @@ class Machine {
|
|
|
4219
4219
|
}
|
|
4220
4220
|
}
|
|
4221
4221
|
else if (data.question === 'peers') {
|
|
4222
|
-
this.worker.postMessage({
|
|
4222
|
+
this.worker.postMessage({
|
|
4223
|
+
id: data.id,
|
|
4224
|
+
input: peernet.connections ? Object.entries(peernet.connections).map(([id, peer]) => peer.toJSON()) : []
|
|
4225
|
+
});
|
|
4223
4226
|
}
|
|
4224
4227
|
else {
|
|
4225
4228
|
this.worker.postMessage({ id: data.id, input: data.input });
|
|
@@ -5198,6 +5201,12 @@ class State extends Contract {
|
|
|
5198
5201
|
get shouldSync() {
|
|
5199
5202
|
if (this.#chainSyncing)
|
|
5200
5203
|
return false;
|
|
5204
|
+
// Check if we have any connected peers with the same version
|
|
5205
|
+
const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
|
|
5206
|
+
if (compatiblePeers.length === 0) {
|
|
5207
|
+
debug$1('No compatible peers available for sync');
|
|
5208
|
+
return false;
|
|
5209
|
+
}
|
|
5201
5210
|
if (!this.#chainSyncing ||
|
|
5202
5211
|
this.#resolveErrored ||
|
|
5203
5212
|
this.#syncState === 'errored' ||
|
|
@@ -5206,6 +5215,25 @@ class State extends Contract {
|
|
|
5206
5215
|
return true;
|
|
5207
5216
|
return false;
|
|
5208
5217
|
}
|
|
5218
|
+
async #waitForPeers(timeoutMs = 30000) {
|
|
5219
|
+
return new Promise((resolve) => {
|
|
5220
|
+
const checkPeers = () => {
|
|
5221
|
+
const peers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
|
|
5222
|
+
if (peers.length > 0) {
|
|
5223
|
+
resolve(true);
|
|
5224
|
+
}
|
|
5225
|
+
};
|
|
5226
|
+
// Check immediately
|
|
5227
|
+
checkPeers();
|
|
5228
|
+
// Set up interval to check periodically
|
|
5229
|
+
const interval = setInterval(checkPeers, 1000);
|
|
5230
|
+
// Set timeout
|
|
5231
|
+
setTimeout(() => {
|
|
5232
|
+
clearInterval(interval);
|
|
5233
|
+
resolve(false);
|
|
5234
|
+
}, timeoutMs);
|
|
5235
|
+
});
|
|
5236
|
+
}
|
|
5209
5237
|
async triggerSync() {
|
|
5210
5238
|
const latest = await this.#getLatestBlock();
|
|
5211
5239
|
return this.syncChain(latest);
|
|
@@ -5265,6 +5293,112 @@ class VersionControl extends State {
|
|
|
5265
5293
|
}
|
|
5266
5294
|
}
|
|
5267
5295
|
|
|
5296
|
+
/**
|
|
5297
|
+
* Connection Monitor - Monitors peer connections and handles reconnection logic
|
|
5298
|
+
*/
|
|
5299
|
+
class ConnectionMonitor {
|
|
5300
|
+
#isMonitoring = false;
|
|
5301
|
+
#checkInterval = null;
|
|
5302
|
+
#reconnectAttempts = 0;
|
|
5303
|
+
#maxReconnectAttempts = 10;
|
|
5304
|
+
#reconnectDelay = 5000;
|
|
5305
|
+
#healthCheckInterval = 10000;
|
|
5306
|
+
get isMonitoring() {
|
|
5307
|
+
return this.#isMonitoring;
|
|
5308
|
+
}
|
|
5309
|
+
get connectedPeers() {
|
|
5310
|
+
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => peer.connected);
|
|
5311
|
+
}
|
|
5312
|
+
get compatiblePeers() {
|
|
5313
|
+
return this.connectedPeers.filter((peer) => peer.version === this.version);
|
|
5314
|
+
}
|
|
5315
|
+
constructor(version) {
|
|
5316
|
+
this.version = version;
|
|
5317
|
+
}
|
|
5318
|
+
start() {
|
|
5319
|
+
if (this.#isMonitoring)
|
|
5320
|
+
return;
|
|
5321
|
+
this.#isMonitoring = true;
|
|
5322
|
+
console.log('🔄 Starting connection monitor...');
|
|
5323
|
+
this.#checkInterval = setInterval(() => {
|
|
5324
|
+
this.#healthCheck();
|
|
5325
|
+
}, this.#healthCheckInterval);
|
|
5326
|
+
// Initial health check
|
|
5327
|
+
this.#healthCheck();
|
|
5328
|
+
}
|
|
5329
|
+
stop() {
|
|
5330
|
+
if (!this.#isMonitoring)
|
|
5331
|
+
return;
|
|
5332
|
+
this.#isMonitoring = false;
|
|
5333
|
+
if (this.#checkInterval) {
|
|
5334
|
+
clearInterval(this.#checkInterval);
|
|
5335
|
+
this.#checkInterval = null;
|
|
5336
|
+
}
|
|
5337
|
+
console.log('⏹️ Connection monitor stopped');
|
|
5338
|
+
}
|
|
5339
|
+
async #healthCheck() {
|
|
5340
|
+
const connectedPeers = this.connectedPeers;
|
|
5341
|
+
const compatiblePeers = this.compatiblePeers;
|
|
5342
|
+
console.log(`🔍 Health check: ${connectedPeers.length} connected, ${compatiblePeers.length} compatible`);
|
|
5343
|
+
if (connectedPeers.length === 0) {
|
|
5344
|
+
console.warn('⚠️ No peer connections detected');
|
|
5345
|
+
await this.#attemptReconnection();
|
|
5346
|
+
}
|
|
5347
|
+
else if (compatiblePeers.length === 0) {
|
|
5348
|
+
console.warn('⚠️ No compatible peers found');
|
|
5349
|
+
// Could attempt to find compatible peers or trigger version negotiation
|
|
5350
|
+
}
|
|
5351
|
+
else {
|
|
5352
|
+
// Reset reconnect attempts on successful connection
|
|
5353
|
+
this.#reconnectAttempts = 0;
|
|
5354
|
+
}
|
|
5355
|
+
// Publish connection status
|
|
5356
|
+
globalThis.pubsub?.publish('connection-status', {
|
|
5357
|
+
connected: connectedPeers.length,
|
|
5358
|
+
compatible: compatiblePeers.length,
|
|
5359
|
+
healthy: compatiblePeers.length > 0
|
|
5360
|
+
});
|
|
5361
|
+
}
|
|
5362
|
+
async #attemptReconnection() {
|
|
5363
|
+
if (this.#reconnectAttempts >= this.#maxReconnectAttempts) {
|
|
5364
|
+
console.error('❌ Max reconnection attempts reached');
|
|
5365
|
+
return;
|
|
5366
|
+
}
|
|
5367
|
+
this.#reconnectAttempts++;
|
|
5368
|
+
console.log(`🔄 Attempting reconnection ${this.#reconnectAttempts}/${this.#maxReconnectAttempts}`);
|
|
5369
|
+
try {
|
|
5370
|
+
// Try to restart the network
|
|
5371
|
+
if (globalThis.peernet?.start) {
|
|
5372
|
+
await globalThis.peernet.start();
|
|
5373
|
+
}
|
|
5374
|
+
// Wait a bit before next check
|
|
5375
|
+
await new Promise((resolve) => setTimeout(resolve, this.#reconnectDelay));
|
|
5376
|
+
}
|
|
5377
|
+
catch (error) {
|
|
5378
|
+
console.error('❌ Reconnection failed:', error.message);
|
|
5379
|
+
// Exponential backoff
|
|
5380
|
+
this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 30000);
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
async waitForPeers(timeoutMs = 30000) {
|
|
5384
|
+
return new Promise((resolve) => {
|
|
5385
|
+
const startTime = Date.now();
|
|
5386
|
+
const checkPeers = () => {
|
|
5387
|
+
if (this.compatiblePeers.length > 0) {
|
|
5388
|
+
resolve(true);
|
|
5389
|
+
return;
|
|
5390
|
+
}
|
|
5391
|
+
if (Date.now() - startTime >= timeoutMs) {
|
|
5392
|
+
resolve(false);
|
|
5393
|
+
return;
|
|
5394
|
+
}
|
|
5395
|
+
setTimeout(checkPeers, 1000);
|
|
5396
|
+
};
|
|
5397
|
+
checkPeers();
|
|
5398
|
+
});
|
|
5399
|
+
}
|
|
5400
|
+
}
|
|
5401
|
+
|
|
5268
5402
|
const debug = globalThis.createDebugger('leofcoin/chain');
|
|
5269
5403
|
// check if browser or local
|
|
5270
5404
|
class Chain extends VersionControl {
|
|
@@ -5277,6 +5411,10 @@ class Chain extends VersionControl {
|
|
|
5277
5411
|
#participants;
|
|
5278
5412
|
#participating;
|
|
5279
5413
|
#jail;
|
|
5414
|
+
#peerConnectionRetries;
|
|
5415
|
+
#maxPeerRetries;
|
|
5416
|
+
#peerRetryDelay;
|
|
5417
|
+
#connectionMonitor;
|
|
5280
5418
|
constructor(config) {
|
|
5281
5419
|
super(config);
|
|
5282
5420
|
this.#slotTime = 10000;
|
|
@@ -5288,6 +5426,9 @@ class Chain extends VersionControl {
|
|
|
5288
5426
|
this.#participants = [];
|
|
5289
5427
|
this.#participating = false;
|
|
5290
5428
|
this.#jail = [];
|
|
5429
|
+
this.#peerConnectionRetries = new Map();
|
|
5430
|
+
this.#maxPeerRetries = 5;
|
|
5431
|
+
this.#peerRetryDelay = 5000;
|
|
5291
5432
|
this.#addTransaction = async (message) => {
|
|
5292
5433
|
const transaction = new TransactionMessage(message);
|
|
5293
5434
|
const hash = await transaction.hash();
|
|
@@ -5362,6 +5503,7 @@ class Chain extends VersionControl {
|
|
|
5362
5503
|
// this.node = await new Node()
|
|
5363
5504
|
this.#participants = [];
|
|
5364
5505
|
this.#participating = false;
|
|
5506
|
+
this.#connectionMonitor = new ConnectionMonitor(this.version);
|
|
5365
5507
|
const initialized = await globalThis.contractStore.has(addresses.contractFactory);
|
|
5366
5508
|
if (!initialized)
|
|
5367
5509
|
await this.#setup();
|
|
@@ -5369,8 +5511,11 @@ class Chain extends VersionControl {
|
|
|
5369
5511
|
// this.#state = new State()
|
|
5370
5512
|
// todo some functions rely on state
|
|
5371
5513
|
await super.init();
|
|
5514
|
+
// Start connection monitoring
|
|
5515
|
+
this.#connectionMonitor.start();
|
|
5372
5516
|
await globalThis.peernet.addRequestHandler('bw-request-message', () => {
|
|
5373
|
-
|
|
5517
|
+
const bw = globalThis.peernet.client?.bw || { up: 0, down: 0 };
|
|
5518
|
+
return new BWMessage(bw);
|
|
5374
5519
|
});
|
|
5375
5520
|
// await globalThis.peernet.addRequestHandler('peerId', () => {
|
|
5376
5521
|
// let node =
|
|
@@ -5451,7 +5596,7 @@ class Chain extends VersionControl {
|
|
|
5451
5596
|
}
|
|
5452
5597
|
}
|
|
5453
5598
|
if (this.wantList.length > 0) {
|
|
5454
|
-
const promises = await Promise.allSettled(this.wantList.map((hash) => peernet.get(hash)));
|
|
5599
|
+
const promises = await Promise.allSettled(this.wantList.map((hash) => peernet.get(hash, 'block')));
|
|
5455
5600
|
for (let i = 0; i < promises.length; i++) {
|
|
5456
5601
|
const result = promises[i];
|
|
5457
5602
|
if (result.status === 'fulfilled')
|
|
@@ -5778,7 +5923,7 @@ class Chain extends VersionControl {
|
|
|
5778
5923
|
* @returns
|
|
5779
5924
|
*/
|
|
5780
5925
|
internalCall(sender, contract, method, parameters) {
|
|
5781
|
-
globalThis.msg = this.#createMessage(sender, contract)
|
|
5926
|
+
// globalThis.msg = this.#createMessage(sender, contract) // Debug line removed
|
|
5782
5927
|
return this.machine.execute(contract, method, parameters);
|
|
5783
5928
|
}
|
|
5784
5929
|
/**
|
|
@@ -5789,11 +5934,11 @@ class Chain extends VersionControl {
|
|
|
5789
5934
|
* @returns
|
|
5790
5935
|
*/
|
|
5791
5936
|
call(contract, method, parameters) {
|
|
5792
|
-
globalThis.msg = this.#createMessage(peernet.selectedAccount, contract)
|
|
5937
|
+
// globalThis.msg = this.#createMessage(peernet.selectedAccount, contract) // Debug line removed
|
|
5793
5938
|
return this.machine.execute(contract, method, parameters);
|
|
5794
5939
|
}
|
|
5795
5940
|
staticCall(contract, method, parameters) {
|
|
5796
|
-
globalThis.msg = this.#createMessage(peernet.selectedAccount, contract)
|
|
5941
|
+
// globalThis.msg = this.#createMessage(peernet.selectedAccount, contract) // Debug line removed
|
|
5797
5942
|
return this.machine.get(contract, method, parameters);
|
|
5798
5943
|
}
|
|
5799
5944
|
mint(to, amount) {
|
|
@@ -5823,6 +5968,27 @@ class Chain extends VersionControl {
|
|
|
5823
5968
|
lookup(name) {
|
|
5824
5969
|
return this.call(addresses.nameService, 'lookup', [name]);
|
|
5825
5970
|
}
|
|
5971
|
+
#monitorPeerConnections() {
|
|
5972
|
+
setInterval(() => {
|
|
5973
|
+
const connectedPeers = Object.values(globalThis.peernet.connections).filter((peer) => peer.connected);
|
|
5974
|
+
debug(`Connected peers: ${connectedPeers.length}`);
|
|
5975
|
+
if (connectedPeers.length === 0) {
|
|
5976
|
+
debug('No peers connected, attempting to reconnect...');
|
|
5977
|
+
this.#attemptPeerReconnection();
|
|
5978
|
+
}
|
|
5979
|
+
}, 10000); // Check every 10 seconds
|
|
5980
|
+
}
|
|
5981
|
+
async #attemptPeerReconnection() {
|
|
5982
|
+
try {
|
|
5983
|
+
// Try to reconnect to star servers
|
|
5984
|
+
if (globalThis.peernet && globalThis.peernet.start) {
|
|
5985
|
+
await globalThis.peernet.start();
|
|
5986
|
+
}
|
|
5987
|
+
}
|
|
5988
|
+
catch (error) {
|
|
5989
|
+
console.warn('Failed to reconnect to peers:', error.message);
|
|
5990
|
+
}
|
|
5991
|
+
}
|
|
5826
5992
|
}
|
|
5827
5993
|
|
|
5828
5994
|
export { Chain as default };
|
|
@@ -8495,7 +8495,7 @@ class Peernet {
|
|
|
8495
8495
|
this.root = options.root;
|
|
8496
8496
|
const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile
|
|
8497
8497
|
// FolderMessageResponse
|
|
8498
|
-
} = await import(/* webpackChunkName: "messages" */ './messages-CW17jRdc-
|
|
8498
|
+
} = await import(/* webpackChunkName: "messages" */ './messages-CW17jRdc-Dtrj-vba.js');
|
|
8499
8499
|
/**
|
|
8500
8500
|
* proto Object containing protos
|
|
8501
8501
|
* @type {Object}
|
|
@@ -8589,7 +8589,7 @@ class Peernet {
|
|
|
8589
8589
|
if (this.#starting || this.#started)
|
|
8590
8590
|
return;
|
|
8591
8591
|
this.#starting = true;
|
|
8592
|
-
const importee = await import('./client-DD7vhDK_-
|
|
8592
|
+
const importee = await import('./client-DD7vhDK_-BdNAdRzV.js');
|
|
8593
8593
|
/**
|
|
8594
8594
|
* @access public
|
|
8595
8595
|
* @type {PeernetClient}
|
|
@@ -9078,7 +9078,7 @@ globalThis.Peernet = Peernet;
|
|
|
9078
9078
|
var networks = {
|
|
9079
9079
|
leofcoin: {
|
|
9080
9080
|
peach: {
|
|
9081
|
-
stars: ['wss://star.leofcoin.org'] //
|
|
9081
|
+
stars: ['wss://star-testnet.leofcoin.org', 'wss://star.leofcoin.org'] // Separate testnet stars
|
|
9082
9082
|
}
|
|
9083
9083
|
}
|
|
9084
9084
|
};
|
package/exports/chain.js
CHANGED
|
@@ -365,7 +365,10 @@ class Machine {
|
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
367
|
else if (data.question === 'peers') {
|
|
368
|
-
this.worker.postMessage({
|
|
368
|
+
this.worker.postMessage({
|
|
369
|
+
id: data.id,
|
|
370
|
+
input: peernet.connections ? Object.entries(peernet.connections).map(([id, peer]) => peer.toJSON()) : []
|
|
371
|
+
});
|
|
369
372
|
}
|
|
370
373
|
else {
|
|
371
374
|
this.worker.postMessage({ id: data.id, input: data.input });
|
|
@@ -1344,6 +1347,12 @@ class State extends Contract {
|
|
|
1344
1347
|
get shouldSync() {
|
|
1345
1348
|
if (this.#chainSyncing)
|
|
1346
1349
|
return false;
|
|
1350
|
+
// Check if we have any connected peers with the same version
|
|
1351
|
+
const compatiblePeers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
|
|
1352
|
+
if (compatiblePeers.length === 0) {
|
|
1353
|
+
debug$1('No compatible peers available for sync');
|
|
1354
|
+
return false;
|
|
1355
|
+
}
|
|
1347
1356
|
if (!this.#chainSyncing ||
|
|
1348
1357
|
this.#resolveErrored ||
|
|
1349
1358
|
this.#syncState === 'errored' ||
|
|
@@ -1352,6 +1361,25 @@ class State extends Contract {
|
|
|
1352
1361
|
return true;
|
|
1353
1362
|
return false;
|
|
1354
1363
|
}
|
|
1364
|
+
async #waitForPeers(timeoutMs = 30000) {
|
|
1365
|
+
return new Promise((resolve) => {
|
|
1366
|
+
const checkPeers = () => {
|
|
1367
|
+
const peers = Object.values(globalThis.peernet.connections || {}).filter((peer) => peer.connected && peer.version === this.version);
|
|
1368
|
+
if (peers.length > 0) {
|
|
1369
|
+
resolve(true);
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
// Check immediately
|
|
1373
|
+
checkPeers();
|
|
1374
|
+
// Set up interval to check periodically
|
|
1375
|
+
const interval = setInterval(checkPeers, 1000);
|
|
1376
|
+
// Set timeout
|
|
1377
|
+
setTimeout(() => {
|
|
1378
|
+
clearInterval(interval);
|
|
1379
|
+
resolve(false);
|
|
1380
|
+
}, timeoutMs);
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1355
1383
|
async triggerSync() {
|
|
1356
1384
|
const latest = await this.#getLatestBlock();
|
|
1357
1385
|
return this.syncChain(latest);
|
|
@@ -1411,6 +1439,112 @@ class VersionControl extends State {
|
|
|
1411
1439
|
}
|
|
1412
1440
|
}
|
|
1413
1441
|
|
|
1442
|
+
/**
|
|
1443
|
+
* Connection Monitor - Monitors peer connections and handles reconnection logic
|
|
1444
|
+
*/
|
|
1445
|
+
class ConnectionMonitor {
|
|
1446
|
+
#isMonitoring = false;
|
|
1447
|
+
#checkInterval = null;
|
|
1448
|
+
#reconnectAttempts = 0;
|
|
1449
|
+
#maxReconnectAttempts = 10;
|
|
1450
|
+
#reconnectDelay = 5000;
|
|
1451
|
+
#healthCheckInterval = 10000;
|
|
1452
|
+
get isMonitoring() {
|
|
1453
|
+
return this.#isMonitoring;
|
|
1454
|
+
}
|
|
1455
|
+
get connectedPeers() {
|
|
1456
|
+
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => peer.connected);
|
|
1457
|
+
}
|
|
1458
|
+
get compatiblePeers() {
|
|
1459
|
+
return this.connectedPeers.filter((peer) => peer.version === this.version);
|
|
1460
|
+
}
|
|
1461
|
+
constructor(version) {
|
|
1462
|
+
this.version = version;
|
|
1463
|
+
}
|
|
1464
|
+
start() {
|
|
1465
|
+
if (this.#isMonitoring)
|
|
1466
|
+
return;
|
|
1467
|
+
this.#isMonitoring = true;
|
|
1468
|
+
console.log('🔄 Starting connection monitor...');
|
|
1469
|
+
this.#checkInterval = setInterval(() => {
|
|
1470
|
+
this.#healthCheck();
|
|
1471
|
+
}, this.#healthCheckInterval);
|
|
1472
|
+
// Initial health check
|
|
1473
|
+
this.#healthCheck();
|
|
1474
|
+
}
|
|
1475
|
+
stop() {
|
|
1476
|
+
if (!this.#isMonitoring)
|
|
1477
|
+
return;
|
|
1478
|
+
this.#isMonitoring = false;
|
|
1479
|
+
if (this.#checkInterval) {
|
|
1480
|
+
clearInterval(this.#checkInterval);
|
|
1481
|
+
this.#checkInterval = null;
|
|
1482
|
+
}
|
|
1483
|
+
console.log('⏹️ Connection monitor stopped');
|
|
1484
|
+
}
|
|
1485
|
+
async #healthCheck() {
|
|
1486
|
+
const connectedPeers = this.connectedPeers;
|
|
1487
|
+
const compatiblePeers = this.compatiblePeers;
|
|
1488
|
+
console.log(`🔍 Health check: ${connectedPeers.length} connected, ${compatiblePeers.length} compatible`);
|
|
1489
|
+
if (connectedPeers.length === 0) {
|
|
1490
|
+
console.warn('⚠️ No peer connections detected');
|
|
1491
|
+
await this.#attemptReconnection();
|
|
1492
|
+
}
|
|
1493
|
+
else if (compatiblePeers.length === 0) {
|
|
1494
|
+
console.warn('⚠️ No compatible peers found');
|
|
1495
|
+
// Could attempt to find compatible peers or trigger version negotiation
|
|
1496
|
+
}
|
|
1497
|
+
else {
|
|
1498
|
+
// Reset reconnect attempts on successful connection
|
|
1499
|
+
this.#reconnectAttempts = 0;
|
|
1500
|
+
}
|
|
1501
|
+
// Publish connection status
|
|
1502
|
+
globalThis.pubsub?.publish('connection-status', {
|
|
1503
|
+
connected: connectedPeers.length,
|
|
1504
|
+
compatible: compatiblePeers.length,
|
|
1505
|
+
healthy: compatiblePeers.length > 0
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
async #attemptReconnection() {
|
|
1509
|
+
if (this.#reconnectAttempts >= this.#maxReconnectAttempts) {
|
|
1510
|
+
console.error('❌ Max reconnection attempts reached');
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
this.#reconnectAttempts++;
|
|
1514
|
+
console.log(`🔄 Attempting reconnection ${this.#reconnectAttempts}/${this.#maxReconnectAttempts}`);
|
|
1515
|
+
try {
|
|
1516
|
+
// Try to restart the network
|
|
1517
|
+
if (globalThis.peernet?.start) {
|
|
1518
|
+
await globalThis.peernet.start();
|
|
1519
|
+
}
|
|
1520
|
+
// Wait a bit before next check
|
|
1521
|
+
await new Promise((resolve) => setTimeout(resolve, this.#reconnectDelay));
|
|
1522
|
+
}
|
|
1523
|
+
catch (error) {
|
|
1524
|
+
console.error('❌ Reconnection failed:', error.message);
|
|
1525
|
+
// Exponential backoff
|
|
1526
|
+
this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 30000);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
async waitForPeers(timeoutMs = 30000) {
|
|
1530
|
+
return new Promise((resolve) => {
|
|
1531
|
+
const startTime = Date.now();
|
|
1532
|
+
const checkPeers = () => {
|
|
1533
|
+
if (this.compatiblePeers.length > 0) {
|
|
1534
|
+
resolve(true);
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
if (Date.now() - startTime >= timeoutMs) {
|
|
1538
|
+
resolve(false);
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
setTimeout(checkPeers, 1000);
|
|
1542
|
+
};
|
|
1543
|
+
checkPeers();
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1414
1548
|
const debug = globalThis.createDebugger('leofcoin/chain');
|
|
1415
1549
|
// check if browser or local
|
|
1416
1550
|
class Chain extends VersionControl {
|
|
@@ -1423,6 +1557,10 @@ class Chain extends VersionControl {
|
|
|
1423
1557
|
#participants;
|
|
1424
1558
|
#participating;
|
|
1425
1559
|
#jail;
|
|
1560
|
+
#peerConnectionRetries;
|
|
1561
|
+
#maxPeerRetries;
|
|
1562
|
+
#peerRetryDelay;
|
|
1563
|
+
#connectionMonitor;
|
|
1426
1564
|
constructor(config) {
|
|
1427
1565
|
super(config);
|
|
1428
1566
|
this.#slotTime = 10000;
|
|
@@ -1434,6 +1572,9 @@ class Chain extends VersionControl {
|
|
|
1434
1572
|
this.#participants = [];
|
|
1435
1573
|
this.#participating = false;
|
|
1436
1574
|
this.#jail = [];
|
|
1575
|
+
this.#peerConnectionRetries = new Map();
|
|
1576
|
+
this.#maxPeerRetries = 5;
|
|
1577
|
+
this.#peerRetryDelay = 5000;
|
|
1437
1578
|
this.#addTransaction = async (message) => {
|
|
1438
1579
|
const transaction = new TransactionMessage(message);
|
|
1439
1580
|
const hash = await transaction.hash();
|
|
@@ -1508,6 +1649,7 @@ class Chain extends VersionControl {
|
|
|
1508
1649
|
// this.node = await new Node()
|
|
1509
1650
|
this.#participants = [];
|
|
1510
1651
|
this.#participating = false;
|
|
1652
|
+
this.#connectionMonitor = new ConnectionMonitor(this.version);
|
|
1511
1653
|
const initialized = await globalThis.contractStore.has(addresses.contractFactory);
|
|
1512
1654
|
if (!initialized)
|
|
1513
1655
|
await this.#setup();
|
|
@@ -1515,8 +1657,11 @@ class Chain extends VersionControl {
|
|
|
1515
1657
|
// this.#state = new State()
|
|
1516
1658
|
// todo some functions rely on state
|
|
1517
1659
|
await super.init();
|
|
1660
|
+
// Start connection monitoring
|
|
1661
|
+
this.#connectionMonitor.start();
|
|
1518
1662
|
await globalThis.peernet.addRequestHandler('bw-request-message', () => {
|
|
1519
|
-
|
|
1663
|
+
const bw = globalThis.peernet.client?.bw || { up: 0, down: 0 };
|
|
1664
|
+
return new BWMessage(bw);
|
|
1520
1665
|
});
|
|
1521
1666
|
// await globalThis.peernet.addRequestHandler('peerId', () => {
|
|
1522
1667
|
// let node =
|
|
@@ -1597,7 +1742,7 @@ class Chain extends VersionControl {
|
|
|
1597
1742
|
}
|
|
1598
1743
|
}
|
|
1599
1744
|
if (this.wantList.length > 0) {
|
|
1600
|
-
const promises = await Promise.allSettled(this.wantList.map((hash) => peernet.get(hash)));
|
|
1745
|
+
const promises = await Promise.allSettled(this.wantList.map((hash) => peernet.get(hash, 'block')));
|
|
1601
1746
|
for (let i = 0; i < promises.length; i++) {
|
|
1602
1747
|
const result = promises[i];
|
|
1603
1748
|
if (result.status === 'fulfilled')
|
|
@@ -1924,7 +2069,7 @@ class Chain extends VersionControl {
|
|
|
1924
2069
|
* @returns
|
|
1925
2070
|
*/
|
|
1926
2071
|
internalCall(sender, contract, method, parameters) {
|
|
1927
|
-
globalThis.msg = this.#createMessage(sender, contract)
|
|
2072
|
+
// globalThis.msg = this.#createMessage(sender, contract) // Debug line removed
|
|
1928
2073
|
return this.machine.execute(contract, method, parameters);
|
|
1929
2074
|
}
|
|
1930
2075
|
/**
|
|
@@ -1935,11 +2080,11 @@ class Chain extends VersionControl {
|
|
|
1935
2080
|
* @returns
|
|
1936
2081
|
*/
|
|
1937
2082
|
call(contract, method, parameters) {
|
|
1938
|
-
globalThis.msg = this.#createMessage(peernet.selectedAccount, contract)
|
|
2083
|
+
// globalThis.msg = this.#createMessage(peernet.selectedAccount, contract) // Debug line removed
|
|
1939
2084
|
return this.machine.execute(contract, method, parameters);
|
|
1940
2085
|
}
|
|
1941
2086
|
staticCall(contract, method, parameters) {
|
|
1942
|
-
globalThis.msg = this.#createMessage(peernet.selectedAccount, contract)
|
|
2087
|
+
// globalThis.msg = this.#createMessage(peernet.selectedAccount, contract) // Debug line removed
|
|
1943
2088
|
return this.machine.get(contract, method, parameters);
|
|
1944
2089
|
}
|
|
1945
2090
|
mint(to, amount) {
|
|
@@ -1969,6 +2114,27 @@ class Chain extends VersionControl {
|
|
|
1969
2114
|
lookup(name) {
|
|
1970
2115
|
return this.call(addresses.nameService, 'lookup', [name]);
|
|
1971
2116
|
}
|
|
2117
|
+
#monitorPeerConnections() {
|
|
2118
|
+
setInterval(() => {
|
|
2119
|
+
const connectedPeers = Object.values(globalThis.peernet.connections).filter((peer) => peer.connected);
|
|
2120
|
+
debug(`Connected peers: ${connectedPeers.length}`);
|
|
2121
|
+
if (connectedPeers.length === 0) {
|
|
2122
|
+
debug('No peers connected, attempting to reconnect...');
|
|
2123
|
+
this.#attemptPeerReconnection();
|
|
2124
|
+
}
|
|
2125
|
+
}, 10000); // Check every 10 seconds
|
|
2126
|
+
}
|
|
2127
|
+
async #attemptPeerReconnection() {
|
|
2128
|
+
try {
|
|
2129
|
+
// Try to reconnect to star servers
|
|
2130
|
+
if (globalThis.peernet && globalThis.peernet.start) {
|
|
2131
|
+
await globalThis.peernet.start();
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
catch (error) {
|
|
2135
|
+
console.warn('Failed to reconnect to peers:', error.message);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
1972
2138
|
}
|
|
1973
2139
|
|
|
1974
2140
|
export { Chain as default };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Monitor - Monitors peer connections and handles reconnection logic
|
|
3
|
+
*/
|
|
4
|
+
export default class ConnectionMonitor {
|
|
5
|
+
#private;
|
|
6
|
+
private version;
|
|
7
|
+
get isMonitoring(): boolean;
|
|
8
|
+
get connectedPeers(): import("@netpeer/swarm/peer").default[];
|
|
9
|
+
get compatiblePeers(): import("@netpeer/swarm/peer").default[];
|
|
10
|
+
constructor(version: string);
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
waitForPeers(timeoutMs?: number): Promise<boolean>;
|
|
14
|
+
}
|
|
@@ -7,7 +7,8 @@ export default class SyncController {
|
|
|
7
7
|
get fullyLoaded(): boolean;
|
|
8
8
|
constructor();
|
|
9
9
|
/**
|
|
10
|
-
* Resolves/rejects a promise or rejects on timeout
|
|
10
|
+
* Resolves/rejects a promise or rejects on timeout with retry logic
|
|
11
11
|
*/
|
|
12
|
-
resolve():
|
|
12
|
+
resolve(operation: () => Promise<any>, timeoutMs?: number): Promise<any>;
|
|
13
|
+
stop(): void;
|
|
13
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leofcoin/chain",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.74",
|
|
4
4
|
"description": "Official javascript implementation",
|
|
5
5
|
"private": false,
|
|
6
6
|
"exports": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@leofcoin/messages": "^1.4.40",
|
|
70
70
|
"@leofcoin/multi-wallet": "^3.1.8",
|
|
71
71
|
"@leofcoin/networks": "^1.1.25",
|
|
72
|
-
"@leofcoin/peernet": "^1.1.
|
|
72
|
+
"@leofcoin/peernet": "^1.1.84",
|
|
73
73
|
"@leofcoin/storage": "^3.5.38",
|
|
74
74
|
"@leofcoin/utils": "^1.1.39",
|
|
75
75
|
"@leofcoin/workers": "^1.5.23",
|