@leofcoin/chain 1.10.13 → 1.10.15
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/beacon-envelope.js +2 -2
- package/exports/beacon-epoch.js +1 -1
- package/exports/beacon-lifecycle.js +2 -2
- package/exports/beacon-round.js +1 -1
- package/exports/beacon.js +1 -1
- package/exports/browser/beacon-envelope.js +79 -3
- package/exports/browser/beacon-epoch.js +2 -1
- package/exports/browser/beacon-lifecycle.js +3 -2
- package/exports/browser/beacon-round.js +2 -1
- package/exports/browser/beacon-wire.js +3 -3
- package/exports/browser/beacon.js +3 -2
- package/exports/browser/{browser-DvU1xNFS-sdlKNCJc.js → browser-DvU1xNFS-DmmNUdE-.js} +4 -4
- package/exports/browser/{proposal.proto-BcUgd885.js → browser-crypto-B9N9KlL4.js} +35 -6
- package/exports/browser/chain.js +16 -295
- package/exports/browser/{client-B-jyclOB-CsNYfj4Z.js → client-DDC8C510-3uaB70KL.js} +36 -12
- package/exports/browser/connection-monitor-GPztgSSJ.js +454 -0
- package/exports/browser/hkdf-CnZvwpZ9.js +222 -0
- package/exports/browser/{index-Bxr5Iztg-C6xBk0rK.js → index-Bxr5Iztg-WbFIosyf.js} +4 -4
- package/exports/browser/{index-CFBGfpG1.js → index-Ccn5sExe.js} +1 -2
- package/exports/browser/{messages-FMRAS8QX-CvlZBzy5.js → messages-DH0tgNM9-C2CtyLsf.js} +4 -4
- package/exports/browser/{node-browser-xlqSBOaN.js → node-browser-BYi7D_oa.js} +6 -6
- package/exports/browser/node-browser.js +4 -4
- package/exports/browser/sha2-piimb6xL.js +614 -0
- package/exports/browser/{weierstrass-C-jX_jly.js → weierstrass-Q0QND1ZT.js} +1 -1
- package/exports/browser/workers/machine-worker.js +412 -135
- package/exports/chain.js +13 -291
- package/exports/connection-monitor.js +324 -0
- package/exports/node.js +7 -0
- package/exports/workers/machine-worker.js +412 -135
- package/package.json +8 -4
- package/exports/browser/browser-CWeoyGUw-BabtHowB.js +0 -57
- package/exports/browser/constants-Cv0p224A.js +0 -130
- package/exports/browser/hkdf-DhdhLAAv.js +0 -147
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { i as inflate_1, c as createDebugger, d as deflate_1, L as LittlePubSub } from './node-browser-
|
|
2
|
-
import 'crypto';
|
|
3
|
-
import './
|
|
4
|
-
import './proposal.proto-BcUgd885.js';
|
|
1
|
+
import { i as inflate_1, c as createDebugger, d as deflate_1, L as LittlePubSub } from './node-browser-BYi7D_oa.js';
|
|
2
|
+
import './browser-crypto-B9N9KlL4.js';
|
|
3
|
+
import './connection-monitor-GPztgSSJ.js';
|
|
5
4
|
import './index-CvKt4UDE.js';
|
|
5
|
+
import './sha2-piimb6xL.js';
|
|
6
6
|
|
|
7
7
|
class Api {
|
|
8
8
|
_pubsub;
|
|
@@ -233,7 +233,7 @@ class SocketRequestClient {
|
|
|
233
233
|
const init = async () => {
|
|
234
234
|
// @ts-ignore
|
|
235
235
|
if (!globalThis.WebSocket && !this.#experimentalWebsocket)
|
|
236
|
-
globalThis.WebSocket = (await import('./browser-DvU1xNFS-
|
|
236
|
+
globalThis.WebSocket = (await import('./browser-DvU1xNFS-DmmNUdE-.js').then(function (n) { return n.b; })).default.w3cwebsocket;
|
|
237
237
|
const client = new WebSocket(this.#url, this.#protocol);
|
|
238
238
|
if (this.#experimentalWebsocket) {
|
|
239
239
|
client.addEventListener('error', this.onerror);
|
|
@@ -1155,6 +1155,7 @@ class Client {
|
|
|
1155
1155
|
#connectEvent = 'peer:connected';
|
|
1156
1156
|
#supportedTransports = new Set(['webrtc']);
|
|
1157
1157
|
#preferredTransportKind = 'webrtc';
|
|
1158
|
+
#wrtcBackend = 'vandeurenglenn';
|
|
1158
1159
|
#fallbackTransportOrder = [];
|
|
1159
1160
|
#enableCircuitFallback = true;
|
|
1160
1161
|
#transportConnectTimeoutMs = 10_000;
|
|
@@ -1212,6 +1213,13 @@ class Client {
|
|
|
1212
1213
|
? 'webrtc'
|
|
1213
1214
|
: 'webtransport';
|
|
1214
1215
|
this.#webTransportUrlTemplate = options.transport?.webtransport?.urlTemplate;
|
|
1216
|
+
const wrtcBackend = options.transport?.webrtc?.backend ||
|
|
1217
|
+
globalThis.process?.env?.NETPEER_SWARM_WRTC_BACKEND ||
|
|
1218
|
+
'vandeurenglenn';
|
|
1219
|
+
if (wrtcBackend !== 'vandeurenglenn' && wrtcBackend !== 'koush') {
|
|
1220
|
+
throw new Error(`Unsupported WebRTC backend: ${wrtcBackend}`);
|
|
1221
|
+
}
|
|
1222
|
+
this.#wrtcBackend = wrtcBackend;
|
|
1215
1223
|
this.#enableCircuitFallback = options.transport?.fallback?.enabled ?? true;
|
|
1216
1224
|
if (typeof options.transport?.fallback?.connectTimeoutMs === 'number' &&
|
|
1217
1225
|
options.transport.fallback.connectTimeoutMs > 0) {
|
|
@@ -1297,7 +1305,8 @@ class Client {
|
|
|
1297
1305
|
throw lastErr;
|
|
1298
1306
|
}
|
|
1299
1307
|
async _init() {
|
|
1300
|
-
if (
|
|
1308
|
+
if (this.#supportedTransports.has('webrtc') &&
|
|
1309
|
+
!globalThis.RTCPeerConnection &&
|
|
1301
1310
|
!globalThis.wrtc) {
|
|
1302
1311
|
const { backend, implementation } = await this.#loadNodeWebrtcImplementation();
|
|
1303
1312
|
globalThis.wrtc = implementation;
|
|
@@ -1325,13 +1334,28 @@ class Client {
|
|
|
1325
1334
|
}
|
|
1326
1335
|
}
|
|
1327
1336
|
async #loadNodeWebrtcImplementation() {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1337
|
+
let wrtcModule;
|
|
1338
|
+
if (this.#wrtcBackend === 'koush') {
|
|
1339
|
+
try {
|
|
1340
|
+
wrtcModule = await import('@koush/wrtc');
|
|
1341
|
+
}
|
|
1342
|
+
catch (error) {
|
|
1343
|
+
throw new Error('The koush WebRTC backend is opt-in; install @koush/wrtc to use it', { cause: error });
|
|
1344
|
+
}
|
|
1332
1345
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1346
|
+
else {
|
|
1347
|
+
wrtcModule = await import('@vandeurenglenn/wrtc');
|
|
1348
|
+
}
|
|
1349
|
+
const moduleCandidate = wrtcModule;
|
|
1350
|
+
const implementation = isWrtcImplementation(moduleCandidate.default)
|
|
1351
|
+
? moduleCandidate.default
|
|
1352
|
+
: wrtcModule;
|
|
1353
|
+
if (!isWrtcImplementation(implementation)) {
|
|
1354
|
+
const packageName = this.#wrtcBackend === 'koush' ? '@koush/wrtc' : '@vandeurenglenn/wrtc';
|
|
1355
|
+
throw new Error(`${packageName} does not match required wrtc contract`);
|
|
1356
|
+
}
|
|
1357
|
+
debug(`using ${this.#wrtcBackend} as wrtc implementation`);
|
|
1358
|
+
return { backend: this.#wrtcBackend, implementation };
|
|
1335
1359
|
}
|
|
1336
1360
|
setupStarListeners(starConnection, starId) {
|
|
1337
1361
|
// create stable references to handlers so we can unsubscribe later
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { F as FormatInterface, o as proto$2, q as proto$3, i as index, f as index$1, r as proto$4 } from './browser-crypto-B9N9KlL4.js';
|
|
2
|
+
|
|
3
|
+
class ValidatorMessage extends FormatInterface {
|
|
4
|
+
get messageName() {
|
|
5
|
+
return "ValidatorMessage";
|
|
6
|
+
}
|
|
7
|
+
constructor(buffer) {
|
|
8
|
+
if (buffer instanceof ValidatorMessage)
|
|
9
|
+
return buffer;
|
|
10
|
+
const name = "validator-message";
|
|
11
|
+
super(buffer, proto$2, { name });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class BlockMessage extends FormatInterface {
|
|
16
|
+
get messageName() {
|
|
17
|
+
return "BlockMessage";
|
|
18
|
+
}
|
|
19
|
+
constructor(buffer) {
|
|
20
|
+
if (buffer instanceof BlockMessage)
|
|
21
|
+
return buffer;
|
|
22
|
+
const name = "block-message";
|
|
23
|
+
super(buffer, proto$3, { name });
|
|
24
|
+
}
|
|
25
|
+
encode(decoded) {
|
|
26
|
+
decoded = decoded || this.decoded;
|
|
27
|
+
const validators = [];
|
|
28
|
+
for (const validator of decoded.validators) {
|
|
29
|
+
if (validator instanceof ValidatorMessage)
|
|
30
|
+
validators.push(validator.encode());
|
|
31
|
+
else
|
|
32
|
+
validators.push(new ValidatorMessage(validator).encode());
|
|
33
|
+
}
|
|
34
|
+
return super.encode({
|
|
35
|
+
...decoded,
|
|
36
|
+
validators: index(validators)
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
decode(encoded) {
|
|
40
|
+
encoded = encoded || this.encoded;
|
|
41
|
+
super.decode(encoded);
|
|
42
|
+
this.decoded.validators = index$1(this.decoded.validators).map((validator) => new ValidatorMessage(validator).decoded);
|
|
43
|
+
return this.decoded;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var proto$1 = {
|
|
48
|
+
up: Number(),
|
|
49
|
+
down: Number()
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
class BWMessage extends FormatInterface {
|
|
53
|
+
get messageName() {
|
|
54
|
+
return "BWMessage";
|
|
55
|
+
}
|
|
56
|
+
constructor(buffer) {
|
|
57
|
+
if (buffer instanceof BWMessage)
|
|
58
|
+
return buffer;
|
|
59
|
+
const name = "bw-message";
|
|
60
|
+
super(buffer, proto$1, { name });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class LastBlockMessage extends FormatInterface {
|
|
65
|
+
get messageName() {
|
|
66
|
+
return "LastBlockMessage";
|
|
67
|
+
}
|
|
68
|
+
constructor(buffer) {
|
|
69
|
+
if (buffer instanceof LastBlockMessage)
|
|
70
|
+
return buffer;
|
|
71
|
+
const name = "last-block-message";
|
|
72
|
+
super(buffer, proto$4, { name });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var proto = {
|
|
77
|
+
lastblock: Object(),
|
|
78
|
+
values: Object()
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
class StateMessage extends FormatInterface {
|
|
82
|
+
get messageName() {
|
|
83
|
+
return "StateMessage";
|
|
84
|
+
}
|
|
85
|
+
constructor(buffer) {
|
|
86
|
+
if (buffer instanceof StateMessage)
|
|
87
|
+
return buffer;
|
|
88
|
+
const name = "state-message";
|
|
89
|
+
super(buffer, proto, { name });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var networks = {
|
|
94
|
+
leofcoin: {
|
|
95
|
+
mainnet: {
|
|
96
|
+
// ports don't really matter since it is favorable to have it begind a ngninx proxy but if we change something to the proto it's easier maybe?
|
|
97
|
+
port: 44444,
|
|
98
|
+
// todo a versionhash would be nice to have as a double check?
|
|
99
|
+
versionHash: "0",
|
|
100
|
+
// a short description identifying the version
|
|
101
|
+
description: "Main net current version",
|
|
102
|
+
stars: ["wss://star.leofcoin.org"]
|
|
103
|
+
// todo webrtc and bittorent stars
|
|
104
|
+
},
|
|
105
|
+
peach: {
|
|
106
|
+
faucet: "https://remote.leofcoin.org/faucet",
|
|
107
|
+
port: 44444,
|
|
108
|
+
description: "Main testnet: latest step before merging into main",
|
|
109
|
+
versionHash: "1",
|
|
110
|
+
stars: ["wss://star.leofcoin.org"]
|
|
111
|
+
// todo webrtc and bittorent stars
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
var networks$1 = /*#__PURE__*/Object.freeze({
|
|
117
|
+
__proto__: null,
|
|
118
|
+
default: networks
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const PROTOCOL_VERSION = "0.3.0";
|
|
122
|
+
const REACHED_ONE_ZERO_ZERO = false;
|
|
123
|
+
const DEFAULT_NODE_OPTIONS = {
|
|
124
|
+
autoStart: false,
|
|
125
|
+
network: "leofcoin:peach",
|
|
126
|
+
networkVersion: "peach",
|
|
127
|
+
version: PROTOCOL_VERSION,
|
|
128
|
+
stars: networks.leofcoin.peach.stars
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const INITIAL_RECONNECT_DELAY = 5e3;
|
|
132
|
+
const MAX_RECONNECT_DELAY = 3e4;
|
|
133
|
+
const isSendablePeer = (peer) => {
|
|
134
|
+
if (!peer || typeof peer !== "object") return false;
|
|
135
|
+
const candidate = peer;
|
|
136
|
+
if (typeof candidate.send !== "function") return false;
|
|
137
|
+
if (candidate.connected === true) return true;
|
|
138
|
+
return typeof candidate.once === "function" && typeof candidate.removeListener === "function";
|
|
139
|
+
};
|
|
140
|
+
const guardPeerMessages = (peernet) => {
|
|
141
|
+
const sendMessage = peernet.sendMessage.bind(peernet);
|
|
142
|
+
peernet.sendMessage = (peer, ...args) => {
|
|
143
|
+
if (isSendablePeer(peer)) return sendMessage(peer, ...args);
|
|
144
|
+
for (const [peerId, connection] of Object.entries(peernet.connections || {})) {
|
|
145
|
+
if (connection === peer) delete peernet.connections[peerId];
|
|
146
|
+
}
|
|
147
|
+
return Promise.resolve(void 0);
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
class ConnectionMonitor {
|
|
151
|
+
#isMonitoring = false;
|
|
152
|
+
#checkInterval = null;
|
|
153
|
+
#reconnectDelay = INITIAL_RECONNECT_DELAY;
|
|
154
|
+
#reconnectTimer = null;
|
|
155
|
+
#healthCheckInterval = 6e4;
|
|
156
|
+
#version;
|
|
157
|
+
#lastHealthCheckAt = 0;
|
|
158
|
+
#reconnecting = false;
|
|
159
|
+
// event handlers to remove later
|
|
160
|
+
#onOnline = null;
|
|
161
|
+
#onVisibilityChange = null;
|
|
162
|
+
#onSigcont = null;
|
|
163
|
+
#onPeerConnected = null;
|
|
164
|
+
get isMonitoring() {
|
|
165
|
+
return this.#isMonitoring;
|
|
166
|
+
}
|
|
167
|
+
get connectedPeers() {
|
|
168
|
+
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => peer.connected);
|
|
169
|
+
}
|
|
170
|
+
get compatiblePeers() {
|
|
171
|
+
return this.connectedPeers.filter((peer) => {
|
|
172
|
+
if (!peer.version || !this.#version) return false;
|
|
173
|
+
const [peerMajor, peerMinor] = peer.version.split(".");
|
|
174
|
+
const [localMajor, localMinor] = this.#version.split(".");
|
|
175
|
+
return peerMajor === localMajor && peerMinor === localMinor;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
get disconnectedPeers() {
|
|
179
|
+
return Object.values(globalThis.peernet?.connections || {}).filter((peer) => !peer.connected);
|
|
180
|
+
}
|
|
181
|
+
#pruneInvalidConnections() {
|
|
182
|
+
const connections = globalThis.peernet?.connections;
|
|
183
|
+
if (!connections) return;
|
|
184
|
+
for (const [peerId, peer] of Object.entries(connections)) {
|
|
185
|
+
if (!isSendablePeer(peer)) {
|
|
186
|
+
delete connections[peerId];
|
|
187
|
+
console.warn(`Removed invalid peer connection: ${peerId}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
start(version) {
|
|
192
|
+
this.#version = version;
|
|
193
|
+
console.log(`\u{1F517} Connection Monitor initialized for version: ${this.#version}`);
|
|
194
|
+
if (this.#isMonitoring) return;
|
|
195
|
+
this.#isMonitoring = true;
|
|
196
|
+
console.log("\u{1F504} Starting connection monitor...");
|
|
197
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
198
|
+
this.#onOnline = () => {
|
|
199
|
+
console.log("\u{1F310} Network online \u2014 attempting restore");
|
|
200
|
+
void this.#restoreNetwork();
|
|
201
|
+
};
|
|
202
|
+
window.addEventListener("online", this.#onOnline);
|
|
203
|
+
this.#onVisibilityChange = () => {
|
|
204
|
+
if (document.visibilityState === "visible") {
|
|
205
|
+
console.log("\u{1F4A1} Visibility regained");
|
|
206
|
+
if (this.connectedPeers.length === 0) {
|
|
207
|
+
console.log("\u{1F4A1} Visibility regained \u2014 attempting restore");
|
|
208
|
+
void this.#restoreNetwork();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
document.addEventListener("visibilitychange", this.#onVisibilityChange);
|
|
213
|
+
}
|
|
214
|
+
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
215
|
+
this.#onSigcont = () => {
|
|
216
|
+
console.log("\u{1F514} Process resumed (SIGCONT) \u2014 attempting restore");
|
|
217
|
+
void this.#restoreNetwork();
|
|
218
|
+
};
|
|
219
|
+
try {
|
|
220
|
+
process.on("SIGCONT", this.#onSigcont);
|
|
221
|
+
} catch (e) {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
this.#lastHealthCheckAt = Date.now();
|
|
225
|
+
this.#checkInterval = setInterval(() => {
|
|
226
|
+
this.#healthCheck();
|
|
227
|
+
}, this.#healthCheckInterval);
|
|
228
|
+
this.#onPeerConnected = () => {
|
|
229
|
+
if (this.#reconnectTimer) {
|
|
230
|
+
clearTimeout(this.#reconnectTimer);
|
|
231
|
+
this.#reconnectTimer = null;
|
|
232
|
+
}
|
|
233
|
+
this.#reconnecting = false;
|
|
234
|
+
this.#reconnectDelay = INITIAL_RECONNECT_DELAY;
|
|
235
|
+
};
|
|
236
|
+
globalThis.pubsub?.subscribe("peer:connected", this.#onPeerConnected);
|
|
237
|
+
}
|
|
238
|
+
stop() {
|
|
239
|
+
if (!this.#isMonitoring) return;
|
|
240
|
+
this.#isMonitoring = false;
|
|
241
|
+
if (this.#checkInterval) {
|
|
242
|
+
clearInterval(this.#checkInterval);
|
|
243
|
+
this.#checkInterval = null;
|
|
244
|
+
}
|
|
245
|
+
if (typeof window !== "undefined") {
|
|
246
|
+
if (this.#onOnline) {
|
|
247
|
+
window.removeEventListener("online", this.#onOnline);
|
|
248
|
+
this.#onOnline = null;
|
|
249
|
+
}
|
|
250
|
+
if (this.#onVisibilityChange) {
|
|
251
|
+
document.removeEventListener("visibilitychange", this.#onVisibilityChange);
|
|
252
|
+
this.#onVisibilityChange = null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (typeof process !== "undefined" && typeof process.removeListener === "function" && this.#onSigcont) {
|
|
256
|
+
try {
|
|
257
|
+
process.removeListener("SIGCONT", this.#onSigcont);
|
|
258
|
+
} catch (e) {
|
|
259
|
+
}
|
|
260
|
+
this.#onSigcont = null;
|
|
261
|
+
}
|
|
262
|
+
if (this.#onPeerConnected) {
|
|
263
|
+
globalThis.pubsub?.unsubscribe("peer:connected", this.#onPeerConnected);
|
|
264
|
+
this.#onPeerConnected = null;
|
|
265
|
+
}
|
|
266
|
+
if (this.#reconnectTimer) {
|
|
267
|
+
clearTimeout(this.#reconnectTimer);
|
|
268
|
+
this.#reconnectTimer = null;
|
|
269
|
+
}
|
|
270
|
+
console.log("\u23F9\uFE0F Connection monitor stopped");
|
|
271
|
+
}
|
|
272
|
+
async #healthCheck() {
|
|
273
|
+
this.#pruneInvalidConnections();
|
|
274
|
+
const now = Date.now();
|
|
275
|
+
const expectedNext = this.#lastHealthCheckAt + this.#healthCheckInterval;
|
|
276
|
+
const drift = now - expectedNext;
|
|
277
|
+
this.#lastHealthCheckAt = now;
|
|
278
|
+
if (drift > 5e3) {
|
|
279
|
+
console.log(`\u23F0 Detected timer drift of ${drift}ms (likely system sleep) \u2014 attempting restore`);
|
|
280
|
+
void this.#restoreNetwork();
|
|
281
|
+
}
|
|
282
|
+
const connectedPeers = this.connectedPeers;
|
|
283
|
+
const compatiblePeers = this.compatiblePeers;
|
|
284
|
+
const disconnectedPeers = this.disconnectedPeers;
|
|
285
|
+
console.log(
|
|
286
|
+
`\u{1F50D} Health check: ${connectedPeers.length} connected, ${compatiblePeers.length} compatible, ${disconnectedPeers.length} negotiating`
|
|
287
|
+
);
|
|
288
|
+
if (connectedPeers.length === 0 && disconnectedPeers.length === 0 && !this.#reconnectTimer) {
|
|
289
|
+
console.warn("\u26A0\uFE0F No peer connections detected \u2014 attempting reconnection");
|
|
290
|
+
await this.#attemptReconnection();
|
|
291
|
+
} else if (compatiblePeers.length === 0 && connectedPeers.length > 0 && !this.#reconnectTimer) {
|
|
292
|
+
console.warn("\u26A0\uFE0F No compatible peers found \u2014 attempting reconnection");
|
|
293
|
+
await this.#attemptReconnection();
|
|
294
|
+
}
|
|
295
|
+
globalThis.pubsub?.publish("connection-status", {
|
|
296
|
+
connected: connectedPeers.length,
|
|
297
|
+
compatible: compatiblePeers.length,
|
|
298
|
+
healthy: compatiblePeers.length > 0
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
// lightweight TCP probe to detect internet connectivity in Node.js
|
|
302
|
+
async #isOnLine(timeout = 1500) {
|
|
303
|
+
if (navigator?.onLine !== void 0) {
|
|
304
|
+
return navigator.onLine;
|
|
305
|
+
}
|
|
306
|
+
return new Promise(async (resolve) => {
|
|
307
|
+
try {
|
|
308
|
+
const net = await import('net');
|
|
309
|
+
const socket = new net.Socket();
|
|
310
|
+
let finished = false;
|
|
311
|
+
const finish = (val) => {
|
|
312
|
+
if (finished) return;
|
|
313
|
+
finished = true;
|
|
314
|
+
try {
|
|
315
|
+
socket.destroy();
|
|
316
|
+
} catch (e) {
|
|
317
|
+
}
|
|
318
|
+
resolve(val);
|
|
319
|
+
};
|
|
320
|
+
socket.setTimeout(timeout);
|
|
321
|
+
socket.once("connect", () => finish(true));
|
|
322
|
+
socket.once("error", () => finish(false));
|
|
323
|
+
socket.once("timeout", () => finish(false));
|
|
324
|
+
socket.connect(53, "1.1.1.1");
|
|
325
|
+
} catch (e) {
|
|
326
|
+
resolve(false);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
// Called on visibility/online/resume events
|
|
331
|
+
async #restoreNetwork() {
|
|
332
|
+
if (this.#reconnecting) {
|
|
333
|
+
console.log("\u{1F501} Reconnection already in progress, skipping");
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
this.#reconnecting = true;
|
|
337
|
+
if (this.connectedPeers.length > 0) {
|
|
338
|
+
console.log("\u2705 Already connected to peers, skipping restoration");
|
|
339
|
+
this.#reconnecting = false;
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
console.log("\u{1F501} Restoring network");
|
|
343
|
+
try {
|
|
344
|
+
const online = await this.#isOnLine(1500);
|
|
345
|
+
if (!online) {
|
|
346
|
+
console.warn("\u26A0\uFE0F No internet detected, skipping restore");
|
|
347
|
+
this.#reconnecting = false;
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
} catch (e) {
|
|
351
|
+
console.warn("\u26A0\uFE0F Online probe failed, proceeding with restore", e?.message || e);
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
console.log("\u{1F504} Attempting network restoration...");
|
|
355
|
+
if (globalThis.peernet?.client?.reinit) {
|
|
356
|
+
console.log(" \u2192 Trying client.reinit()");
|
|
357
|
+
try {
|
|
358
|
+
await globalThis.peernet.client.reinit();
|
|
359
|
+
console.log(" \u2705 client.reinit() succeeded");
|
|
360
|
+
} catch (e) {
|
|
361
|
+
console.warn(" \u26A0\uFE0F client.reinit() failed:", e?.message || e);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (globalThis.peernet?.start) {
|
|
365
|
+
console.log(" \u2192 Trying peernet.start()");
|
|
366
|
+
try {
|
|
367
|
+
await globalThis.peernet.start();
|
|
368
|
+
console.log(" \u2705 peernet.start() succeeded");
|
|
369
|
+
} catch (e) {
|
|
370
|
+
console.warn(" \u26A0\uFE0F peernet.start() failed:", e?.message || e);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
try {
|
|
374
|
+
const networkName = globalThis.peernet?.network;
|
|
375
|
+
if (networkName && typeof networkName === "string") {
|
|
376
|
+
const { default: networks } = await Promise.resolve().then(function () { return networks$1; });
|
|
377
|
+
const [mainKey, subKey] = networkName.split(":");
|
|
378
|
+
const networkConfig = networks?.[mainKey]?.[subKey];
|
|
379
|
+
if (networkConfig?.stars && Array.isArray(networkConfig.stars)) {
|
|
380
|
+
console.log(" \u2192 Attempting to dial star servers:", networkConfig.stars.join(", "));
|
|
381
|
+
for (const star of networkConfig.stars) {
|
|
382
|
+
try {
|
|
383
|
+
if (globalThis.peernet?.client && "dial" in globalThis.peernet.client) {
|
|
384
|
+
await globalThis.peernet.client.dial(star);
|
|
385
|
+
console.log(` \u2705 Connected to star server: ${star}`);
|
|
386
|
+
}
|
|
387
|
+
} catch (e) {
|
|
388
|
+
console.warn(` \u26A0\uFE0F Failed to dial ${star}:`, e?.message || e);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
} catch (e) {
|
|
394
|
+
console.warn(" \u26A0\uFE0F Could not load or dial star servers:", e?.message || e);
|
|
395
|
+
}
|
|
396
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
397
|
+
this.#pruneInvalidConnections();
|
|
398
|
+
const connectedAfter = this.connectedPeers.length;
|
|
399
|
+
console.log(`\u{1F504} Restoration complete. Connected peers: ${connectedAfter}`);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.error("\u274C Network restoration failed:", error?.message || error);
|
|
402
|
+
} finally {
|
|
403
|
+
this.#reconnecting = false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
async waitForPeers(timeoutMs = 3e4) {
|
|
407
|
+
return new Promise((resolve) => {
|
|
408
|
+
const startTime = Date.now();
|
|
409
|
+
const checkPeers = () => {
|
|
410
|
+
if (this.compatiblePeers.length > 0) {
|
|
411
|
+
resolve(true);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (Date.now() - startTime >= timeoutMs) {
|
|
415
|
+
resolve(false);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
setTimeout(checkPeers, 1e3);
|
|
419
|
+
};
|
|
420
|
+
checkPeers();
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
async #attemptReconnection() {
|
|
424
|
+
try {
|
|
425
|
+
await this.#restoreNetwork();
|
|
426
|
+
const hasConnections = this.connectedPeers.length > 0 || this.disconnectedPeers.length > 0;
|
|
427
|
+
if (hasConnections) {
|
|
428
|
+
console.log("\u2705 Reconnection successful, resetting backoff delay");
|
|
429
|
+
this.#reconnectDelay = INITIAL_RECONNECT_DELAY;
|
|
430
|
+
} else {
|
|
431
|
+
console.warn("\u26A0\uFE0F Reconnection attempt completed but no peers connected");
|
|
432
|
+
this.#scheduleReconnect();
|
|
433
|
+
}
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.error("\u274C Reconnection failed:", error?.message || error);
|
|
436
|
+
this.#scheduleReconnect();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
#scheduleReconnect() {
|
|
440
|
+
if (!this.#isMonitoring || this.#reconnectTimer) return;
|
|
441
|
+
if (this.#reconnectDelay >= MAX_RECONNECT_DELAY) {
|
|
442
|
+
console.warn("\u26A0\uFE0F No peers available; automatic retries paused until the next health check or network event");
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, MAX_RECONNECT_DELAY);
|
|
446
|
+
console.warn(`\u26A0\uFE0F Increasing reconnection delay to ${this.#reconnectDelay} ms`);
|
|
447
|
+
this.#reconnectTimer = setTimeout(() => {
|
|
448
|
+
this.#reconnectTimer = null;
|
|
449
|
+
void this.#attemptReconnection();
|
|
450
|
+
}, this.#reconnectDelay);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export { BlockMessage as B, ConnectionMonitor as C, DEFAULT_NODE_OPTIONS as D, LastBlockMessage as L, PROTOCOL_VERSION as P, REACHED_ONE_ZERO_ZERO as R, StateMessage as S, ValidatorMessage as V, BWMessage as a, guardPeerMessages as g };
|