@electrum-cash/network 3.3.0 → 4.0.0-development.6391574314

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/dist/index.cjs DELETED
@@ -1,1637 +0,0 @@
1
- var $790lB$events = require("events");
2
- var $790lB$asyncmutex = require("async-mutex");
3
- var $790lB$debug = require("debug");
4
- var $790lB$losslessjson = require("lossless-json");
5
- var $790lB$tls = require("tls");
6
- var $790lB$net = require("net");
7
- var $790lB$monsterbitarisomorphicws = require("@monsterbitar/isomorphic-ws");
8
-
9
-
10
- function $parcel$exportWildcard(dest, source) {
11
- Object.keys(source).forEach(function(key) {
12
- if (key === 'default' || key === '__esModule' || dest.hasOwnProperty(key)) {
13
- return;
14
- }
15
-
16
- Object.defineProperty(dest, key, {
17
- enumerable: true,
18
- get: function get() {
19
- return source[key];
20
- }
21
- });
22
- });
23
-
24
- return dest;
25
- }
26
-
27
- function $parcel$export(e, n, v, s) {
28
- Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
29
- }
30
-
31
- function $parcel$interopDefault(a) {
32
- return a && a.__esModule ? a.default : a;
33
- }
34
-
35
- $parcel$export(module.exports, "ElectrumClient", () => $ab5ca7f40897fe20$export$2e2bcd8739ae039);
36
- $parcel$export(module.exports, "ElectrumCluster", () => $683dbcc250bd73d7$export$2e2bcd8739ae039);
37
-
38
- // Create the debug logs.
39
- const $8677acc278daa2d1$var$debug = {
40
- client: (0, ($parcel$interopDefault($790lB$debug)))("electrum-cash:client "),
41
- cluster: (0, ($parcel$interopDefault($790lB$debug)))("electrum-cash:cluster"),
42
- errors: (0, ($parcel$interopDefault($790lB$debug)))("electrum-cash:error "),
43
- warning: (0, ($parcel$interopDefault($790lB$debug)))("electrum-cash:warning"),
44
- network: (0, ($parcel$interopDefault($790lB$debug)))("electrum-cash:network"),
45
- ping: (0, ($parcel$interopDefault($790lB$debug)))("electrum-cash:pulses ")
46
- };
47
- // Set log colors.
48
- $8677acc278daa2d1$var$debug.client.color = "2";
49
- $8677acc278daa2d1$var$debug.cluster.color = "3";
50
- $8677acc278daa2d1$var$debug.errors.color = "9";
51
- $8677acc278daa2d1$var$debug.warning.color = "13";
52
- $8677acc278daa2d1$var$debug.network.color = "4";
53
- $8677acc278daa2d1$var$debug.ping.color = "8";
54
- var // Export the logs.
55
- $8677acc278daa2d1$export$2e2bcd8739ae039 = $8677acc278daa2d1$var$debug;
56
-
57
-
58
-
59
- /**
60
- * Grouping of utilities that simplifies implementation of the Electrum protocol.
61
- *
62
- * @ignore
63
- */ class $fc4d4e072b89ce45$var$ElectrumProtocol {
64
- /**
65
- * Helper function that builds an Electrum request object.
66
- *
67
- * @param {string} method method to call.
68
- * @param {array} parameters method parameters for the call.
69
- * @param {string} requestId unique string or number referencing this request.
70
- *
71
- * @returns a properly formatted Electrum request string.
72
- */ static buildRequestObject(method, parameters, requestId) {
73
- // Return the formatted request object.
74
- // NOTE: Electrum either uses JsonRPC strictly or loosely.
75
- // If we specify protocol identifier without being 100% compliant, we risk being disconnected/blacklisted.
76
- // For this reason, we omit the protocol identifier to avoid issues.
77
- return JSON.stringify({
78
- method: method,
79
- params: parameters,
80
- id: requestId
81
- });
82
- }
83
- /**
84
- * Constant used to verify if a provided string is a valid version number.
85
- *
86
- * @returns a regular expression that matches valid version numbers.
87
- */ static get versionRegexp() {
88
- return /^\d+(\.\d+)+$/;
89
- }
90
- /**
91
- * Constant used to separate statements/messages in a stream of data.
92
- *
93
- * @returns the delimiter used by Electrum to separate statements.
94
- */ static get statementDelimiter() {
95
- return "\n";
96
- }
97
- }
98
- var // export the protocol.
99
- $fc4d4e072b89ce45$export$2e2bcd8739ae039 = $fc4d4e072b89ce45$var$ElectrumProtocol;
100
-
101
-
102
- var $5fc020d2a13224ff$exports = {};
103
-
104
- $parcel$export($5fc020d2a13224ff$exports, "isVersionRejected", () => $5fc020d2a13224ff$export$e1f38ab2b4ebdde6);
105
- $parcel$export($5fc020d2a13224ff$exports, "isVersionNegotiated", () => $5fc020d2a13224ff$export$9598f0c76aa41d73);
106
- const $5fc020d2a13224ff$export$e1f38ab2b4ebdde6 = function(object) {
107
- return "error" in object;
108
- };
109
- const $5fc020d2a13224ff$export$9598f0c76aa41d73 = function(object) {
110
- return "software" in object && "protocol" in object;
111
- };
112
-
113
-
114
- var $48ce8effa4fcde4d$exports = {};
115
-
116
- $parcel$export($48ce8effa4fcde4d$exports, "ElectrumTransport", () => $48ce8effa4fcde4d$export$d048df559e6d3842);
117
- $parcel$export($48ce8effa4fcde4d$exports, "DefaultParameters", () => $48ce8effa4fcde4d$export$f019be48b3aacb1a);
118
- var $09f19ceb374f83a1$exports = {};
119
-
120
- $parcel$export($09f19ceb374f83a1$exports, "ClusterOrder", () => $09f19ceb374f83a1$export$161fe3707f756bf9);
121
- $parcel$export($09f19ceb374f83a1$exports, "ClusterDistribution", () => $09f19ceb374f83a1$export$436a960acc41e848);
122
- $parcel$export($09f19ceb374f83a1$exports, "ClusterStatus", () => $09f19ceb374f83a1$export$c66b56bc0ff967ca);
123
- $parcel$export($09f19ceb374f83a1$exports, "ClientState", () => $09f19ceb374f83a1$export$c4f81c6d30ca200f);
124
- $parcel$export($09f19ceb374f83a1$exports, "ConnectionStatus", () => $09f19ceb374f83a1$export$7516420eb880ab68);
125
- // Disable indent rule for this file because it is broken (https://github.com/typescript-eslint/typescript-eslint/issues/1824)
126
- /* eslint-disable @typescript-eslint/indent */ /**
127
- * Enum that denotes the ordering to use in an ElectrumCluster.
128
- * @enum {number}
129
- * @property {0} RANDOM Send requests to randomly selected servers in the cluster.
130
- * @property {1} PRIORITY Send requests to servers in the cluster in the order they were added.
131
- */ var $09f19ceb374f83a1$export$161fe3707f756bf9;
132
- (function(ClusterOrder) {
133
- ClusterOrder[ClusterOrder["RANDOM"] = 0] = "RANDOM";
134
- ClusterOrder[ClusterOrder["PRIORITY"] = 1] = "PRIORITY";
135
- })($09f19ceb374f83a1$export$161fe3707f756bf9 || ($09f19ceb374f83a1$export$161fe3707f756bf9 = {}));
136
- var $09f19ceb374f83a1$export$436a960acc41e848;
137
- (function(ClusterDistribution) {
138
- ClusterDistribution[ClusterDistribution["ALL"] = 0] = "ALL";
139
- })($09f19ceb374f83a1$export$436a960acc41e848 || ($09f19ceb374f83a1$export$436a960acc41e848 = {}));
140
- var $09f19ceb374f83a1$export$c66b56bc0ff967ca;
141
- (function(ClusterStatus) {
142
- ClusterStatus[ClusterStatus["DISABLED"] = 0] = "DISABLED";
143
- ClusterStatus[ClusterStatus["DEGRADED"] = 1] = "DEGRADED";
144
- ClusterStatus[ClusterStatus["READY"] = 2] = "READY";
145
- })($09f19ceb374f83a1$export$c66b56bc0ff967ca || ($09f19ceb374f83a1$export$c66b56bc0ff967ca = {}));
146
- var $09f19ceb374f83a1$export$c4f81c6d30ca200f;
147
- (function(ClientState) {
148
- ClientState[ClientState["UNAVAILABLE"] = 0] = "UNAVAILABLE";
149
- ClientState[ClientState["AVAILABLE"] = 1] = "AVAILABLE";
150
- })($09f19ceb374f83a1$export$c4f81c6d30ca200f || ($09f19ceb374f83a1$export$c4f81c6d30ca200f = {}));
151
- var $09f19ceb374f83a1$export$7516420eb880ab68;
152
- (function(ConnectionStatus) {
153
- ConnectionStatus[ConnectionStatus["DISCONNECTED"] = 0] = "DISCONNECTED";
154
- ConnectionStatus[ConnectionStatus["CONNECTED"] = 1] = "CONNECTED";
155
- ConnectionStatus[ConnectionStatus["DISCONNECTING"] = 2] = "DISCONNECTING";
156
- ConnectionStatus[ConnectionStatus["CONNECTING"] = 3] = "CONNECTING";
157
- ConnectionStatus[ConnectionStatus["RECONNECTING"] = 4] = "RECONNECTING";
158
- })($09f19ceb374f83a1$export$7516420eb880ab68 || ($09f19ceb374f83a1$export$7516420eb880ab68 = {}));
159
-
160
-
161
- const $48ce8effa4fcde4d$export$d048df559e6d3842 = {
162
- TCP: {
163
- Port: 50001,
164
- Scheme: "tcp"
165
- },
166
- TCP_TLS: {
167
- Port: 50002,
168
- Scheme: "tcp_tls"
169
- },
170
- WS: {
171
- Port: 50003,
172
- Scheme: "ws"
173
- },
174
- WSS: {
175
- Port: 50004,
176
- Scheme: "wss"
177
- }
178
- };
179
- const $48ce8effa4fcde4d$export$f019be48b3aacb1a = {
180
- // Port number for TCP TLS connections
181
- PORT: $48ce8effa4fcde4d$export$d048df559e6d3842.TCP_TLS.Port,
182
- // Transport to connect to the Electrum server
183
- TRANSPORT_SCHEME: $48ce8effa4fcde4d$export$d048df559e6d3842.TCP_TLS.Scheme,
184
- // How long to wait before attempting to reconnect, in milliseconds.
185
- RECONNECT: 5000,
186
- // How long to wait for network operations before following up, in milliseconds.
187
- TIMEOUT: 30000,
188
- // Time between ping messages, in milliseconds. Pinging keeps the connection alive.
189
- // The reason for pinging this frequently is to detect connection problems early.
190
- PING_INTERVAL: 1000,
191
- // How many servers are required before we trust information provided.
192
- CLUSTER_CONFIDENCE: 1,
193
- // How many servers we send requests to.
194
- CLUSTER_DISTRIBUTION: (0, $09f19ceb374f83a1$export$436a960acc41e848).ALL,
195
- // What order we select servers to send requests to.
196
- CLUSTER_ORDER: (0, $09f19ceb374f83a1$export$161fe3707f756bf9).RANDOM,
197
- // If we use BigInt for numbers in json when parsing and returning json response from the server.
198
- USE_BIG_INT: false
199
- };
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
- /**
210
- * Isomorphic Socket interface supporting TCP sockets and WebSockets (Node and browser).
211
- * The interface is a subset of the TLSSocket interface with some slight modifications.
212
- * It can be expanded when more socket functionality is needed in the rest of the
213
- * electrum-cash code. Changes from the TLSSocket interface (besides it being a subset):
214
- * - Event 'close' -> 'disconnect'
215
- * - New function socket.disconnect()
216
- *
217
- * @ignore
218
- */ class $5c1f0c31f90fde04$var$ElectrumSocket extends (0, $790lB$events.EventEmitter) {
219
- /**
220
- * Connect to host:port using the specified transport
221
- *
222
- * @param {string} host Fully qualified domain name or IP address of the host
223
- * @param {number} port Network port for the host to connect to
224
- * @param {TransportScheme} scheme Transport scheme to use
225
- * @param {number} timeout If no connection is established after `timeout` ms, the connection is terminated
226
- *
227
- * @throws {Error} if an incorrect transport scheme is specified
228
- */ connect(host, port, scheme, timeout) {
229
- // Check that no existing socket exists before initiating a new connection.
230
- if (this.tcpSocket || this.webSocket) throw new Error("Cannot initiate a new socket connection when an existing connection exists");
231
- // Set a timer to force disconnect after `timeout` seconds
232
- this.timers.disconnect = setTimeout(()=>this.disconnectOnTimeout(host, port, timeout), timeout);
233
- // Remove the timer if a connection is successfully established
234
- this.once("connect", this.clearDisconnectTimerOnTimeout);
235
- // Define how to refer to the connection scheme in debug output.
236
- const socketTypes = {
237
- [(0, $48ce8effa4fcde4d$export$d048df559e6d3842).TCP.Scheme]: "a TCP Socket",
238
- [(0, $48ce8effa4fcde4d$export$d048df559e6d3842).TCP_TLS.Scheme]: "an encrypted TCP socket",
239
- [(0, $48ce8effa4fcde4d$export$d048df559e6d3842).WS.Scheme]: "a WebSocket",
240
- [(0, $48ce8effa4fcde4d$export$d048df559e6d3842).WSS.Scheme]: "an encrypted WebSocket"
241
- };
242
- // Log that we are trying to establish a connection.
243
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Initiating ${socketTypes[scheme]} connection to '${host}:${port}'.`);
244
- if (scheme === (0, $48ce8effa4fcde4d$export$d048df559e6d3842).TCP.Scheme || scheme === (0, $48ce8effa4fcde4d$export$d048df559e6d3842).TCP_TLS.Scheme) {
245
- if (scheme === (0, $48ce8effa4fcde4d$export$d048df559e6d3842).TCP_TLS.Scheme) {
246
- // Initialize connection options.
247
- const connectionOptions = {
248
- rejectUnauthorized: false
249
- };
250
- // If the hostname is not an IP address..
251
- if (!$790lB$net.isIP(host)) // Set the servername option which enables support for SNI.
252
- // NOTE: SNI enables a server that hosts multiple domains to provide the appropriate TLS certificate.
253
- connectionOptions.serverName = host;
254
- // Initialize this.tcpSocket (allowing self-signed certificates).
255
- this.tcpSocket = $790lB$tls.connect(port, host, connectionOptions);
256
- // Add a 'secureConnect' listener that checks the authorization status of
257
- // the socket, and logs a warning when it uses a self signed certificate.
258
- this.tcpSocket.once("secureConnect", ()=>{
259
- // Cannot happen, since this event callback *only* exists on TLSSocket
260
- if (!(this.tcpSocket instanceof $790lB$tls.TLSSocket)) return;
261
- // Force cast authorizationError from Error to string (through unknown)
262
- // because it is incorrectly typed as an Error
263
- const authorizationError = this.tcpSocket.authorizationError;
264
- if (authorizationError === "DEPTH_ZERO_SELF_SIGNED_CERT") (0, $8677acc278daa2d1$export$2e2bcd8739ae039).warning(`Connection to ${host}:${port} uses a self-signed certificate`);
265
- });
266
- // Trigger successful connection events.
267
- this.tcpSocket.on("secureConnect", this.onConnect.bind(this, socketTypes[scheme], host, port));
268
- } else {
269
- // Initialize this.tcpSocket.
270
- this.tcpSocket = $790lB$net.connect({
271
- host: host,
272
- port: port
273
- });
274
- // Trigger successful connection events.
275
- this.tcpSocket.on("connect", this.onConnect.bind(this, socketTypes[scheme], host, port));
276
- }
277
- // Configure encoding.
278
- this.tcpSocket.setEncoding("utf8");
279
- // Enable persistent connections with an initial delay of 0.
280
- this.tcpSocket.setKeepAlive(true, 0);
281
- // Disable buffering of outgoing data.
282
- this.tcpSocket.setNoDelay(true);
283
- // Forward the encountered errors.
284
- this.tcpSocket.on("error", this.eventForwarders.tcpError);
285
- } else if (scheme === (0, $48ce8effa4fcde4d$export$d048df559e6d3842).WS.Scheme || scheme === (0, $48ce8effa4fcde4d$export$d048df559e6d3842).WSS.Scheme) {
286
- if (scheme === (0, $48ce8effa4fcde4d$export$d048df559e6d3842).WSS.Scheme) // Initialize this.webSocket (rejecting self-signed certificates).
287
- // We reject self-signed certificates to match functionality of browsers.
288
- this.webSocket = new (0, $790lB$monsterbitarisomorphicws.WebSocket)(`wss://${host}:${port}`);
289
- else // Initialize this.webSocket.
290
- this.webSocket = new (0, $790lB$monsterbitarisomorphicws.WebSocket)(`ws://${host}:${port}`);
291
- // Trigger successful connection events.
292
- this.webSocket.addEventListener("open", this.onConnect.bind(this, socketTypes[scheme], host, port));
293
- // Forward the encountered errors.
294
- this.webSocket.addEventListener("error", this.eventForwarders.wsError);
295
- } else // Throw an error if an incorrect transport is specified
296
- throw new Error("Incorrect transport specified");
297
- }
298
- /**
299
- * Sets up forwarding of events related to the connection.
300
- *
301
- * @param {string} connectionType Name of the connection/transport type, used for logging.
302
- * @param {string} host Fully qualified domain name or IP address of the host
303
- * @param {number} port Network port for the host to connect to
304
- */ onConnect(connectionType, host, port) {
305
- // If the onConnect function has already run, do not execute it again.
306
- if (this.onConnectHasRun) return;
307
- // Log that the connection has been established.
308
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Established ${connectionType} connection with '${host}:${port}'.`);
309
- if (typeof this.tcpSocket !== "undefined") {
310
- // Forward the socket events
311
- this.tcpSocket.addListener("close", this.eventForwarders.disconnect);
312
- this.tcpSocket.addListener("data", this.eventForwarders.tcpData);
313
- } else if (typeof this.webSocket !== "undefined") {
314
- // Forward the socket events
315
- this.webSocket.addEventListener("close", this.eventForwarders.disconnect);
316
- this.webSocket.addEventListener("message", this.eventForwarders.wsData);
317
- }
318
- // Indicate that the onConnect function has run.
319
- this.onConnectHasRun = true;
320
- // Emit the connect event.
321
- this.emit("connect");
322
- }
323
- /**
324
- * Clears the disconnect timer if it is still active.
325
- */ clearDisconnectTimerOnTimeout() {
326
- // Clear the retry timer if it is still active.
327
- if (this.timers.disconnect) clearTimeout(this.timers.disconnect);
328
- }
329
- /**
330
- * Forcibly terminate the connection.
331
- *
332
- * @throws {Error} if no connection was found
333
- */ disconnect() {
334
- // Clear the disconnect timer so that the socket does not try to disconnect again later.
335
- this.clearDisconnectTimerOnTimeout();
336
- // Handle disconnect based differently depending on socket type.
337
- if (this.tcpSocket) {
338
- // Remove all event forwarders.
339
- this.tcpSocket.removeListener("close", this.eventForwarders.disconnect);
340
- this.tcpSocket.removeListener("data", this.eventForwarders.tcpData);
341
- this.tcpSocket.removeListener("error", this.eventForwarders.tcpError);
342
- // Terminate the connection.
343
- this.tcpSocket.destroy();
344
- // Remove the stored socket.
345
- this.tcpSocket = undefined;
346
- } else if (this.webSocket) try {
347
- // Remove all event forwarders.
348
- this.webSocket.removeEventListener("close", this.eventForwarders.disconnect);
349
- this.webSocket.removeEventListener("message", this.eventForwarders.wsData);
350
- this.webSocket.removeEventListener("error", this.eventForwarders.wsError);
351
- // Gracefully terminate the connection.
352
- this.webSocket.close();
353
- } catch (ignored) {
354
- // close() will throw an error if the connection has not been established yet.
355
- // We ignore this error, since no similar error gets thrown in the TLS Socket.
356
- } finally{
357
- // Remove the stored socket regardless of any thrown errors.
358
- this.webSocket = undefined;
359
- }
360
- // Indicate that the onConnect function has not run and it has to be run again.
361
- this.onConnectHasRun = false;
362
- // Emit a disconnect event
363
- this.emit("disconnect");
364
- }
365
- /**
366
- * Write data to the socket
367
- *
368
- * @param {Uint8Array | string} data Data to be written to the socket
369
- * @param {function} callback Callback function to be called when the write has completed
370
- *
371
- * @throws {Error} if no connection was found
372
- * @returns true if the message was fully flushed to the socket, false if part of the message
373
- * is queued in the user memory
374
- */ write(data, callback) {
375
- if (this.tcpSocket) // Write data to the TLS Socket and return the status indicating whether the
376
- // full message was flushed to the socket
377
- return this.tcpSocket.write(data, callback);
378
- if (this.webSocket) {
379
- // Write data to the WebSocket
380
- this.webSocket.send(data, callback);
381
- // WebSockets always fit everything in a single request, so we return true
382
- return true;
383
- }
384
- // Throw an error if no active connection is found
385
- throw new Error("Cannot write to socket when there is no active connection");
386
- }
387
- /**
388
- * Force a disconnection if no connection is established after `timeout` milliseconds.
389
- *
390
- * @param {string} host Host of the connection that timed out
391
- * @param {number} port Port of the connection that timed out
392
- * @param {number} timeout Elapsed milliseconds
393
- */ disconnectOnTimeout(host, port, timeout) {
394
- // Remove the connect listener.
395
- this.removeListener("connect", this.clearDisconnectTimerOnTimeout);
396
- // Create a new timeout error.
397
- const timeoutError = {
398
- code: "ETIMEDOUT",
399
- message: `Connection to '${host}:${port}' timed out after ${timeout} milliseconds`
400
- };
401
- // Emit an error event so that connect is rejected upstream.
402
- this.emit("error", timeoutError);
403
- // Forcibly disconnect to clean up the connection on timeout
404
- this.disconnect();
405
- }
406
- constructor(...args){
407
- super(...args);
408
- // Declare timers for keep-alive pings and reconnection
409
- this.timers = {};
410
- // Initialize boolean that indicates whether the onConnect function has run (initialize to false).
411
- this.onConnectHasRun = false;
412
- // Initialize event forwarding functions.
413
- this.eventForwarders = {
414
- disconnect: ()=>this.emit("disconnect"),
415
- tcpData: (data)=>this.emit("data", data),
416
- wsData: (event)=>this.emit("data", `${event.data}\n`),
417
- tcpError: (err)=>this.emit("error", err),
418
- wsError: (event)=>this.emit("error", event.error)
419
- };
420
- }
421
- }
422
- var // export the socket.
423
- $5c1f0c31f90fde04$export$2e2bcd8739ae039 = $5c1f0c31f90fde04$var$ElectrumSocket;
424
-
425
-
426
-
427
-
428
- /**
429
- * Wrapper around TLS/WSS sockets that gracefully separates a network stream into Electrum protocol messages.
430
- *
431
- * @ignore
432
- */ class $e54751b0628f418d$var$ElectrumConnection extends (0, $790lB$events.EventEmitter) {
433
- /**
434
- * Sets up network configuration for an Electrum client connection.
435
- *
436
- * @param {string} application your application name, used to identify to the electrum host.
437
- * @param {string} version protocol version to use with the host.
438
- * @param {string} host fully qualified domain name or IP number of the host.
439
- * @param {number} port the network port of the host.
440
- * @param {TransportScheme} scheme the transport scheme to use for connection
441
- * @param {number} timeout how long network delays we will wait for before taking action, in milliseconds.
442
- * @param {number} pingInterval the time between sending pings to the electrum host, in milliseconds.
443
- * @param {number} reconnectInterval the time between reconnection attempts to the electrum host, in milliseconds.
444
- * @param {boolean} useBigInt whether to use bigint for numbers in json response.
445
- *
446
- * @throws {Error} if `version` is not a valid version string.
447
- */ constructor(application, version, host, port = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).PORT, scheme = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).TRANSPORT_SCHEME, timeout = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).TIMEOUT, pingInterval = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).PING_INTERVAL, reconnectInterval = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).RECONNECT, useBigInt = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).USE_BIG_INT){
448
- // Initialize the event emitter.
449
- super();
450
- this.application = application;
451
- this.version = version;
452
- this.host = host;
453
- this.port = port;
454
- this.scheme = scheme;
455
- this.timeout = timeout;
456
- this.pingInterval = pingInterval;
457
- this.reconnectInterval = reconnectInterval;
458
- this.useBigInt = useBigInt;
459
- this.// Declare timers for keep-alive pings and reconnection
460
- timers = {};
461
- this.// Initialize an empty array of connection verification timers.
462
- // eslint-disable-next-line no-undef
463
- verifications = [];
464
- this.// Initialize the connected flag to false to indicate that there is no connection
465
- status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTED;
466
- this.// Initialize messageBuffer to an empty string
467
- messageBuffer = "";
468
- // Check if the provided version is a valid version number.
469
- if (!(0, $fc4d4e072b89ce45$export$2e2bcd8739ae039).versionRegexp.test(version)) // Throw an error since the version number was not valid.
470
- throw new Error(`Provided version string (${version}) is not a valid protocol version number.`);
471
- // Create an initial network socket.
472
- this.createSocket();
473
- // Handle visibility changes when run in a browser environment.
474
- if (typeof document !== "undefined") document.addEventListener("visibilitychange", this.handleVisibilityChange.bind(this));
475
- }
476
- /**
477
- * Returns a string for the host identifier for usage in debug messages.
478
- */ get hostIdentifier() {
479
- return `${this.host}:${this.port}`;
480
- }
481
- /**
482
- * Create and configures a fresh socket and attaches all relevant listeners.
483
- */ createSocket() {
484
- // Initialize a new ElectrumSocket
485
- this.socket = new (0, $5c1f0c31f90fde04$export$2e2bcd8739ae039)();
486
- // Set up handlers for connection and disconnection.
487
- this.socket.on("connect", this.onSocketConnect.bind(this));
488
- this.socket.on("disconnect", this.onSocketDisconnect.bind(this));
489
- // Set up handler for incoming data.
490
- this.socket.on("data", this.parseMessageChunk.bind(this));
491
- }
492
- /**
493
- * Shuts down and destroys the current socket.
494
- */ destroySocket() {
495
- // Close the socket connection and destroy the socket.
496
- this.socket.disconnect();
497
- }
498
- /**
499
- * Assembles incoming data into statements and hands them off to the message parser.
500
- *
501
- * @param {string} data data to append to the current message buffer, as a string.
502
- *
503
- * @throws {SyntaxError} if the passed statement parts are not valid JSON.
504
- */ parseMessageChunk(data) {
505
- // Update the timestamp for when we last received data.
506
- this.lastReceivedTimestamp = Date.now();
507
- // Clear and remove all verification timers.
508
- this.verifications.forEach((timer)=>clearTimeout(timer));
509
- this.verifications.length = 0;
510
- // Add the message to the current message buffer.
511
- this.messageBuffer += data;
512
- // Check if the new message buffer contains the statement delimiter.
513
- while(this.messageBuffer.includes((0, $fc4d4e072b89ce45$export$2e2bcd8739ae039).statementDelimiter)){
514
- // Split message buffer into statements.
515
- const statementParts = this.messageBuffer.split((0, $fc4d4e072b89ce45$export$2e2bcd8739ae039).statementDelimiter);
516
- // For as long as we still have statements to parse..
517
- while(statementParts.length > 1){
518
- // Move the first statement to its own variable.
519
- const currentStatementList = String(statementParts.shift());
520
- // Parse the statement into an object or list of objects.
521
- let statementList = (0, $790lB$losslessjson.parse)(currentStatementList, null, this.useBigInt ? (0, $790lB$losslessjson.parseNumberAndBigInt) : parseFloat);
522
- // Wrap the statement in an array if it is not already a batched statement list.
523
- if (!Array.isArray(statementList)) statementList = [
524
- statementList
525
- ];
526
- // For as long as there is statements in the result set..
527
- while(statementList.length > 0){
528
- // Move the first statement from the batch to its own variable.
529
- const currentStatement = statementList.shift();
530
- // If the current statement is a version negotiation response..
531
- if (currentStatement.id === "versionNegotiation") {
532
- if (currentStatement.error) // Then emit a failed version negotiation response signal.
533
- this.emit("version", {
534
- error: currentStatement.error
535
- });
536
- else // Emit a successful version negotiation response signal.
537
- this.emit("version", {
538
- software: currentStatement.result[0],
539
- protocol: currentStatement.result[1]
540
- });
541
- continue;
542
- }
543
- // If the current statement is a keep-alive response..
544
- if (currentStatement.id === "keepAlive") continue;
545
- // Emit the statements for handling higher up in the stack.
546
- this.emit("statement", currentStatement);
547
- }
548
- }
549
- // Store the remaining statement as the current message buffer.
550
- this.messageBuffer = statementParts.shift() || "";
551
- }
552
- }
553
- /**
554
- * Sends a keep-alive message to the host.
555
- *
556
- * @returns true if the ping message was fully flushed to the socket, false if
557
- * part of the message is queued in the user memory
558
- */ ping() {
559
- // Write a log message.
560
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).ping(`Sending keep-alive ping to '${this.hostIdentifier}'`);
561
- // Craft a keep-alive message.
562
- const message = (0, $fc4d4e072b89ce45$export$2e2bcd8739ae039).buildRequestObject("server.ping", [], "keepAlive");
563
- // Send the keep-alive message.
564
- const status = this.send(message);
565
- // Return the ping status.
566
- return status;
567
- }
568
- /**
569
- * Initiates the network connection negotiates a protocol version. Also emits the 'connect' signal if successful.
570
- *
571
- * @throws {Error} if the socket connection fails.
572
- * @returns a promise resolving when the connection is established
573
- */ async connect() {
574
- // If we are already connected return true.
575
- if (this.status === (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTED) return;
576
- // Indicate that the connection is connecting
577
- this.status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTING;
578
- // Define a function to wrap connection as a promise.
579
- const connectionResolver = (resolve, reject)=>{
580
- const rejector = (error)=>{
581
- // Set the status back to disconnected
582
- this.status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTED;
583
- // Reject with the error as reason
584
- reject(error);
585
- };
586
- // Replace previous error handlers to reject the promise on failure.
587
- this.socket.removeAllListeners("error");
588
- this.socket.once("error", rejector);
589
- // Define a function to wrap version negotiation as a callback.
590
- const versionNegotiator = ()=>{
591
- // Write a log message to show that we have started version negotiation.
592
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Requesting protocol version ${this.version} with '${this.hostIdentifier}'.`);
593
- // remove the one-time error handler since no error was detected.
594
- this.socket.removeListener("error", rejector);
595
- // Build a version negotiation message.
596
- const versionMessage = (0, $fc4d4e072b89ce45$export$2e2bcd8739ae039).buildRequestObject("server.version", [
597
- this.application,
598
- this.version
599
- ], "versionNegotiation");
600
- // Define a function to wrap version validation as a function.
601
- const versionValidator = (version)=>{
602
- // Check if version negotiation failed.
603
- if ((0, $5fc020d2a13224ff$export$e1f38ab2b4ebdde6)(version)) {
604
- // Disconnect from the host.
605
- this.disconnect(true);
606
- // Declare an error message.
607
- const errorMessage = "unsupported protocol version.";
608
- // Log the error.
609
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).errors(`Failed to connect with ${this.hostIdentifier} due to ${errorMessage}`);
610
- // Reject the connection with false since version negotiation failed.
611
- reject(errorMessage);
612
- } else if (version.protocol !== this.version && `${version.protocol}.0` !== this.version && `${version.protocol}.0.0` !== this.version) {
613
- // Disconnect from the host.
614
- this.disconnect(true);
615
- // Declare an error message.
616
- const errorMessage = `incompatible protocol version negotiated (${version.protocol} !== ${this.version}).`;
617
- // Log the error.
618
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).errors(`Failed to connect with ${this.hostIdentifier} due to ${errorMessage}`);
619
- // Reject the connection with false since version negotiation failed.
620
- reject(errorMessage);
621
- } else {
622
- // Write a log message.
623
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Negotiated protocol version ${version.protocol} with '${this.hostIdentifier}', powered by ${version.software}.`);
624
- // Set connection status to connected
625
- this.status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTED;
626
- // Emit a connect event now that the connection is usable.
627
- this.emit("connect");
628
- // Resolve the connection promise since we successfully connected and negotiated protocol version.
629
- resolve();
630
- }
631
- };
632
- // Listen for version negotiation once.
633
- this.once("version", versionValidator);
634
- // Send the version negotiation message.
635
- this.send(versionMessage);
636
- };
637
- // Prepare the version negotiation.
638
- this.socket.once("connect", versionNegotiator);
639
- // Set up handler for network errors.
640
- this.socket.on("error", this.onSocketError.bind(this));
641
- // Connect to the server.
642
- this.socket.connect(this.host, this.port, this.scheme, this.timeout);
643
- };
644
- // Wait until connection is established and version negotiation succeeds.
645
- await new Promise(connectionResolver);
646
- }
647
- /**
648
- * Restores the network connection.
649
- */ async reconnect() {
650
- // If a reconnect timer is set, remove it
651
- await this.clearReconnectTimer();
652
- // Write a log message.
653
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Trying to reconnect to '${this.hostIdentifier}'..`);
654
- // Set the status to reconnecting for more accurate log messages.
655
- this.status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).RECONNECTING;
656
- // Destroy and recreate the socket to get a clean slate.
657
- this.destroySocket();
658
- this.createSocket();
659
- try {
660
- // Try to connect again.
661
- await this.connect();
662
- } catch (error) {
663
- // Do nothing as the error should be handled via the disconnect and error signals.
664
- }
665
- }
666
- /**
667
- * Removes the current reconnect timer.
668
- */ clearReconnectTimer() {
669
- // If a reconnect timer is set, remove it
670
- if (this.timers.reconnect) clearTimeout(this.timers.reconnect);
671
- // Reset the timer reference.
672
- this.timers.reconnect = undefined;
673
- }
674
- /**
675
- * Removes the current keep-alive timer.
676
- */ clearKeepAliveTimer() {
677
- // If a keep-alive timer is set, remove it
678
- if (this.timers.keepAlive) clearTimeout(this.timers.keepAlive);
679
- // Reset the timer reference.
680
- this.timers.keepAlive = undefined;
681
- }
682
- /**
683
- * Initializes the keep alive timer loop.
684
- */ setupKeepAliveTimer() {
685
- // If the keep-alive timer loop is not currently set up..
686
- if (!this.timers.keepAlive) // Set a new keep-alive timer.
687
- this.timers.keepAlive = setTimeout(this.ping.bind(this), this.pingInterval);
688
- }
689
- /**
690
- * Tears down the current connection and removes all event listeners on disconnect.
691
- *
692
- * @param {boolean} force disconnect even if the connection has not been fully established yet.
693
- * @param {boolean} intentional update connection state if disconnect is intentional.
694
- *
695
- * @returns true if successfully disconnected, or false if there was no connection.
696
- */ async disconnect(force = false, intentional = true) {
697
- // Return early when there is nothing to disconnect from
698
- if (this.status === (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTED && !force) // Return false to indicate that there was nothing to disconnect from.
699
- return false;
700
- // Update connection state if the disconnection is intentional.
701
- // NOTE: The state is meant to represent what the client is requesting, but
702
- // is used internally to handle visibility changes in browsers to ensure functional reconnection.
703
- if (intentional) // Set connection status to null to indicate tear-down is currently happening.
704
- this.status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTING;
705
- // If a keep-alive timer is set, remove it.
706
- await this.clearKeepAliveTimer();
707
- // If a reconnect timer is set, remove it
708
- await this.clearReconnectTimer();
709
- const disconnectResolver = (resolve)=>{
710
- // Resolve to true after the connection emits a disconnect
711
- this.once("disconnect", ()=>resolve(true));
712
- // Close the connection and destroy the socket.
713
- this.destroySocket();
714
- };
715
- // Return true to indicate that we disconnected.
716
- return new Promise(disconnectResolver);
717
- }
718
- /**
719
- * Updates connection state based on application visibility.
720
- *
721
- * Some browsers will disconnect network connections when the browser is out of focus,
722
- * which would normally cause our reconnect-on-timeout routines to trigger, but that
723
- * results in a poor user experience since the events are not handled consistently
724
- * and sometimes it can take some time after restoring focus to the browser.
725
- *
726
- * By manually disconnecting when this happens we prevent the default reconnection routines
727
- * and make the behavior consistent across browsers.
728
- */ async handleVisibilityChange() {
729
- // Disconnect when application is removed from focus.
730
- if (document.visibilityState === "hidden") {
731
- const forceDisconnect = true;
732
- const isIntended = false;
733
- this.disconnect(forceDisconnect, isIntended);
734
- }
735
- // Reconnect when application is returned to focus.
736
- if (document.visibilityState === "visible") this.reconnect();
737
- }
738
- /**
739
- * Sends an arbitrary message to the server.
740
- *
741
- * @param {string} message json encoded request object to send to the server, as a string.
742
- *
743
- * @returns true if the message was fully flushed to the socket, false if part of the message
744
- * is queued in the user memory
745
- */ send(message) {
746
- // Remove the current keep-alive timer if it exists.
747
- this.clearKeepAliveTimer();
748
- // Get the current timestamp in milliseconds.
749
- const currentTime = Date.now();
750
- // Follow up and verify that the message got sent..
751
- const verificationTimer = setTimeout(this.verifySend.bind(this, currentTime), this.timeout);
752
- // Store the verification timer locally so that it can be cleared when data has been received.
753
- this.verifications.push(verificationTimer);
754
- // Set a new keep-alive timer.
755
- this.setupKeepAliveTimer();
756
- // Write the message to the network socket.
757
- return this.socket.write(message + (0, $fc4d4e072b89ce45$export$2e2bcd8739ae039).statementDelimiter);
758
- }
759
- // --- Event managers. --- //
760
- /**
761
- * Marks the connection as timed out and schedules reconnection if we have not
762
- * received data within the expected time frame.
763
- */ verifySend(sentTimestamp) {
764
- // If we haven't received any data since we last sent data out..
765
- if (Number(this.lastReceivedTimestamp) < sentTimestamp) {
766
- // If this connection is already disconnected, we do not change anything
767
- if (this.status === (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTED || this.status === (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTING) {
768
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).errors(`Tried to verify already disconnected connection to '${this.hostIdentifier}'`);
769
- return;
770
- }
771
- // Remove the current keep-alive timer if it exists.
772
- this.clearKeepAliveTimer();
773
- // Write a notification to the logs.
774
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Connection to '${this.hostIdentifier}' timed out.`);
775
- // Close the connection to avoid re-use.
776
- // NOTE: This initiates reconnection routines if the connection has not
777
- // been marked as intentionally disconnected.
778
- this.socket.disconnect();
779
- }
780
- }
781
- /**
782
- * Updates the connection status when a connection is confirmed.
783
- */ onSocketConnect() {
784
- // If a reconnect timer is set, remove it.
785
- this.clearReconnectTimer();
786
- // Set up the initial timestamp for when we last received data from the server.
787
- this.lastReceivedTimestamp = Date.now();
788
- // Set up the initial keep-alive timer.
789
- this.setupKeepAliveTimer();
790
- // Clear all temporary error listeners.
791
- this.socket.removeAllListeners("error");
792
- // Set up handler for network errors.
793
- this.socket.on("error", this.onSocketError.bind(this));
794
- }
795
- /**
796
- * Updates the connection status when a connection is ended.
797
- */ onSocketDisconnect() {
798
- // Send a disconnect signal higher up the stack.
799
- this.emit("disconnect");
800
- // Remove the current keep-alive timer if it exists.
801
- this.clearKeepAliveTimer();
802
- // If this is a connection we're trying to tear down..
803
- if (this.status === (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTING) {
804
- // If a reconnect timer is set, remove it.
805
- this.clearReconnectTimer();
806
- // Remove all event listeners
807
- this.removeAllListeners();
808
- // Mark the connection as disconnected.
809
- this.status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTED;
810
- // Write a log message.
811
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Disconnected from '${this.hostIdentifier}'.`);
812
- } else {
813
- // If this is for an established connection..
814
- if (this.status === (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTED) // Write a notification to the logs.
815
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).errors(`Connection with '${this.hostIdentifier}' was closed, trying to reconnect in ${this.reconnectInterval / 1000} seconds.`);
816
- // Mark the connection as disconnected for now..
817
- this.status = (0, $09f19ceb374f83a1$export$7516420eb880ab68).DISCONNECTED;
818
- // If we don't have a pending reconnection timer..
819
- if (!this.timers.reconnect) // Attempt to reconnect after one keep-alive duration.
820
- this.timers.reconnect = setTimeout(this.reconnect.bind(this), this.reconnectInterval);
821
- }
822
- }
823
- /**
824
- * Notify administrator of any unexpected errors.
825
- */ onSocketError(error) {
826
- // Report a generic error if no error information is present.
827
- // NOTE: When using WSS, the error event explicitly
828
- // only allows to send a "simple" event without data.
829
- // https://stackoverflow.com/a/18804298
830
- if (typeof error === "undefined") // Do nothing, and instead rely on the socket disconnect event for further information.
831
- return;
832
- // If the DNS lookup failed.
833
- if (error.code === "EAI_AGAIN") {
834
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).errors(`Failed to look up DNS records for '${this.host}'.`);
835
- return;
836
- }
837
- // If the connection timed out..
838
- if (error.code === "ETIMEDOUT") {
839
- // Log the provided timeout message.
840
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).errors(error.message);
841
- return;
842
- }
843
- // Log unknown error
844
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).errors(`Unknown network error ('${this.hostIdentifier}'): `, error);
845
- }
846
- }
847
- var // Export the connection.
848
- $e54751b0628f418d$export$2e2bcd8739ae039 = $e54751b0628f418d$var$ElectrumConnection;
849
-
850
-
851
-
852
-
853
-
854
-
855
- // Acceptable parameter types for RPC messages
856
- const $3b2b2f2709a48a20$export$d73a2e87a509880 = function(message) {
857
- return "id" in message && "error" in message;
858
- };
859
- const $3b2b2f2709a48a20$export$81276773828ff315 = function(message) {
860
- return "id" in message && "result" in message;
861
- };
862
- const $3b2b2f2709a48a20$export$280de919a0cf6928 = function(message) {
863
- return !("id" in message) && "method" in message;
864
- };
865
- const $3b2b2f2709a48a20$export$94e3360fcddccc76 = function(message) {
866
- return "id" in message && "method" in message;
867
- };
868
-
869
-
870
-
871
- /**
872
- * Triggers when the underlying connection is established.
873
- *
874
- * @event ElectrumClient#connected
875
- */ /**
876
- * Triggers when the underlying connection is lost.
877
- *
878
- * @event ElectrumClient#disconnected
879
- */ /**
880
- * Triggers when the remote server sends data that is not a request response.
881
- *
882
- * @event ElectrumClient#notification
883
- */ /**
884
- * High-level Electrum client that lets applications send requests and subscribe to notification events from a server.
885
- */ class $ab5ca7f40897fe20$var$ElectrumClient extends (0, $790lB$events.EventEmitter) {
886
- /**
887
- * Initializes an Electrum client.
888
- *
889
- * @param {string} application your application name, used to identify to the electrum host.
890
- * @param {string} version protocol version to use with the host.
891
- * @param {string} host fully qualified domain name or IP number of the host.
892
- * @param {number} port the TCP network port of the host.
893
- * @param {TransportScheme} scheme the transport scheme to use for connection
894
- * @param {number} timeout how long network delays we will wait for before taking action, in milliseconds.
895
- * @param {number} pingInterval the time between sending pings to the electrum host, in milliseconds.
896
- * @param {number} reconnectInterval the time between reconnection attempts to the electrum host, in milliseconds.
897
- * @param {boolean} useBigInt whether to use bigint for numbers in json response.
898
- *
899
- * @throws {Error} if `version` is not a valid version string.
900
- */ constructor(application, version, host, port = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).PORT, scheme = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).TRANSPORT_SCHEME, timeout = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).TIMEOUT, pingInterval = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).PING_INTERVAL, reconnectInterval = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).RECONNECT, useBigInt = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).USE_BIG_INT){
901
- // Initialize the event emitter.
902
- super();
903
- // Initialize an empty list of subscription metadata.
904
- this.subscriptionMethods = {};
905
- // Start counting the request IDs from 0
906
- this.requestId = 0;
907
- // Initialize an empty dictionary for keeping track of request resolvers
908
- this.requestResolvers = {};
909
- // Mutex lock used to prevent simultaneous connect() and disconnect() calls.
910
- this.connectionLock = new (0, $790lB$asyncmutex.Mutex)();
911
- // Set up a connection to an electrum server.
912
- this.connection = new (0, $e54751b0628f418d$export$2e2bcd8739ae039)(application, version, host, port, scheme, timeout, pingInterval, reconnectInterval, useBigInt);
913
- }
914
- /**
915
- * Connects to the remote server.
916
- *
917
- * @throws {Error} if the socket connection fails.
918
- * @returns a promise resolving when the connection is established.
919
- */ async connect() {
920
- // Create a lock so that multiple connects/disconnects cannot race each other.
921
- const unlock = await this.connectionLock.acquire();
922
- try {
923
- // If we are already connected, do not attempt to connect again.
924
- if (this.connection.status === (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTED) return;
925
- // Listen for parsed statements.
926
- this.connection.on("statement", this.response.bind(this));
927
- // Hook up resubscription on connection.
928
- this.connection.on("connect", this.resubscribeOnConnect.bind(this));
929
- // Relay connect and disconnect events.
930
- this.connection.on("connect", this.emit.bind(this, "connected"));
931
- this.connection.on("disconnect", this.onConnectionDisconnect.bind(this));
932
- // Relay error events.
933
- this.connection.on("error", this.emit.bind(this, "error"));
934
- // Connect with the server.
935
- await this.connection.connect();
936
- } finally{
937
- unlock();
938
- }
939
- }
940
- /**
941
- * Disconnects from the remote server and removes all event listeners/subscriptions and open requests.
942
- *
943
- * @param {boolean} force disconnect even if the connection has not been fully established yet.
944
- * @param {boolean} retainSubscriptions retain subscription data so they will be restored on reconnection.
945
- *
946
- * @returns true if successfully disconnected, or false if there was no connection.
947
- */ async disconnect(force = false, retainSubscriptions = false) {
948
- // Create a lock so that multiple connects/disconnects cannot race each other.
949
- const unlock = await this.connectionLock.acquire();
950
- try {
951
- if (!retainSubscriptions) {
952
- // Cancel all event listeners.
953
- this.removeAllListeners();
954
- // Remove all subscription data
955
- this.subscriptionMethods = {};
956
- }
957
- // For each pending request..
958
- for(const index in this.requestResolvers){
959
- // Reject the request.
960
- const requestResolver = this.requestResolvers[index];
961
- requestResolver(new Error("Manual disconnection"));
962
- // Remove the request.
963
- delete this.requestResolvers[index];
964
- }
965
- // Disconnect from the remove server.
966
- return await this.connection.disconnect(force);
967
- } finally{
968
- unlock();
969
- }
970
- }
971
- /**
972
- * Calls a method on the remote server with the supplied parameters.
973
- *
974
- * @param {string} method name of the method to call.
975
- * @param {...string} parameters one or more parameters for the method.
976
- *
977
- * @throws {Error} if the client is disconnected.
978
- * @returns a promise that resolves with the result of the method or an Error.
979
- */ async request(method, ...parameters) {
980
- // If we are not connected to a server..
981
- if (this.connection.status !== (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTED) // Reject the request with a disconnected error message.
982
- throw new Error(`Unable to send request to a disconnected server '${this.connection.host}'.`);
983
- // Increase the request ID by one.
984
- this.requestId += 1;
985
- // Store a copy of the request id.
986
- const id = this.requestId;
987
- // Format the arguments as an electrum request object.
988
- const message = (0, $fc4d4e072b89ce45$export$2e2bcd8739ae039).buildRequestObject(method, parameters, id);
989
- // Define a function to wrap the request in a promise.
990
- const requestResolver = (resolve)=>{
991
- // Add a request resolver for this promise to the list of requests.
992
- this.requestResolvers[id] = (error, data)=>{
993
- // If the resolution failed..
994
- if (error) // Resolve the promise with the error for the application to handle.
995
- resolve(error);
996
- else // Resolve the promise with the request results.
997
- resolve(data);
998
- };
999
- // Send the request message to the remote server.
1000
- this.connection.send(message);
1001
- };
1002
- // Write a log message.
1003
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).network(`Sending request '${method}' to '${this.connection.host}'`);
1004
- // return a promise to deliver results later.
1005
- return new Promise(requestResolver);
1006
- }
1007
- /**
1008
- * Subscribes to the method and payload at the server.
1009
- *
1010
- * @note the response for the subscription request is issued as a notification event.
1011
- *
1012
- * @param {string} method one of the subscribable methods the server supports.
1013
- * @param {...string} parameters one or more parameters for the method.
1014
- *
1015
- * @throws {Error} if the client is disconnected.
1016
- * @returns a promise resolving when the subscription is established.
1017
- */ async subscribe(method, ...parameters) {
1018
- // Initialize an empty list of subscription payloads, if needed.
1019
- if (!this.subscriptionMethods[method]) this.subscriptionMethods[method] = new Set();
1020
- // Store the subscription parameters to track what data we have subscribed to.
1021
- this.subscriptionMethods[method].add(JSON.stringify(parameters));
1022
- // Send initial subscription request.
1023
- const requestData = await this.request(method, ...parameters);
1024
- // Construct a notification structure to package the initial result as a notification.
1025
- const notification = {
1026
- jsonrpc: "2.0",
1027
- method: method,
1028
- params: [
1029
- ...parameters,
1030
- requestData
1031
- ]
1032
- };
1033
- // Manually emit an event for the initial response.
1034
- this.emit("notification", notification);
1035
- }
1036
- /**
1037
- * Unsubscribes to the method at the server and removes any callback functions
1038
- * when there are no more subscriptions for the method.
1039
- *
1040
- * @param {string} method a previously subscribed to method.
1041
- * @param {...string} parameters one or more parameters for the method.
1042
- *
1043
- * @throws {Error} if no subscriptions exist for the combination of the provided `method` and `parameters.
1044
- * @throws {Error} if the client is disconnected.
1045
- * @returns a promise resolving when the subscription is removed.
1046
- */ async unsubscribe(method, ...parameters) {
1047
- // Throw an error if the client is disconnected.
1048
- if (this.connection.status !== (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTED) throw new Error(`Unable to send unsubscribe request to a disconnected server '${this.connection.host}'.`);
1049
- // If this method has no subscriptions..
1050
- if (!this.subscriptionMethods[method]) // Reject this promise with an explanation.
1051
- throw new Error(`Cannot unsubscribe from '${method}' since the method has no subscriptions.`);
1052
- // Pack up the parameters as a long string.
1053
- const subscriptionParameters = JSON.stringify(parameters);
1054
- // If the method payload could not be located..
1055
- if (!this.subscriptionMethods[method].has(subscriptionParameters)) // Reject this promise with an explanation.
1056
- throw new Error(`Cannot unsubscribe from '${method}' since it has no subscription with the given parameters.`);
1057
- // Remove this specific subscription payload from internal tracking.
1058
- this.subscriptionMethods[method].delete(subscriptionParameters);
1059
- // Send unsubscription request to the server
1060
- // NOTE: As a convenience we allow users to define the method as the subscribe or unsubscribe version.
1061
- await this.request(method.replace(".subscribe", ".unsubscribe"), ...parameters);
1062
- // Write a log message.
1063
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).client(`Unsubscribed from '${String(method)}' for the '${subscriptionParameters}' parameters.`);
1064
- }
1065
- /**
1066
- * Restores existing subscriptions without updating status or triggering manual callbacks.
1067
- *
1068
- * @throws {Error} if subscription data cannot be found for all stored event names.
1069
- * @throws {Error} if the client is disconnected.
1070
- * @returns a promise resolving to true when the subscriptions are restored.
1071
- *
1072
- * @ignore
1073
- */ async resubscribeOnConnect() {
1074
- // Write a log message.
1075
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).client(`Connected to '${this.connection.hostIdentifier}'.`);
1076
- // Initialize an empty list of resubscription promises.
1077
- const resubscriptionPromises = [];
1078
- // For each method we have a subscription for..
1079
- for(const method in this.subscriptionMethods){
1080
- // .. and for each parameter we have previously been subscribed to..
1081
- for (const parameterJSON of this.subscriptionMethods[method].values()){
1082
- // restore the parameters from JSON.
1083
- const parameters = JSON.parse(parameterJSON);
1084
- // Send a subscription request.
1085
- resubscriptionPromises.push(this.subscribe(method, ...parameters));
1086
- }
1087
- // Wait for all re-subscriptions to complete.
1088
- await Promise.all(resubscriptionPromises);
1089
- }
1090
- // Write a log message if there was any subscriptions to restore.
1091
- if (resubscriptionPromises.length > 0) (0, $8677acc278daa2d1$export$2e2bcd8739ae039).client(`Restored ${resubscriptionPromises.length} previous subscriptions for '${this.connection.hostIdentifier}'`);
1092
- }
1093
- /**
1094
- * Parser messages from the remote server to resolve request promises and emit subscription events.
1095
- *
1096
- * @param {RPCNotification | RPCResponse} message the response message
1097
- *
1098
- * @throws {Error} if the message ID does not match an existing request.
1099
- * @ignore
1100
- */ response(message) {
1101
- // If the received message is a notification, we forward it to all event listeners
1102
- if ((0, $3b2b2f2709a48a20$export$280de919a0cf6928)(message)) {
1103
- // Write a log message.
1104
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).client(`Received notification for '${message.method}' from '${this.connection.host}'`);
1105
- // Forward the message content to all event listeners.
1106
- this.emit("notification", message);
1107
- // Return since it does not have an associated request resolver
1108
- return;
1109
- }
1110
- // If the response ID is null we cannot use it to index our request resolvers
1111
- if (message.id === null) // Throw an internal error, this should not happen.
1112
- throw new Error("Internal error: Received an RPC response with ID null.");
1113
- // Look up which request promise we should resolve this.
1114
- const requestResolver = this.requestResolvers[message.id];
1115
- // If we do not have a request resolver for this response message..
1116
- if (!requestResolver) // Throw an internal error, this should not happen.
1117
- throw new Error("Internal error: Callback for response not available.");
1118
- // Remove the promise from the request list.
1119
- delete this.requestResolvers[message.id];
1120
- // If the message contains an error..
1121
- if ((0, $3b2b2f2709a48a20$export$d73a2e87a509880)(message)) // Forward the message error to the request resolver and omit the `result` parameter.
1122
- requestResolver(new Error(message.error.message));
1123
- else // Forward the message content to the request resolver and omit the `error` parameter
1124
- // (by setting it to undefined).
1125
- requestResolver(undefined, message.result);
1126
- }
1127
- /**
1128
- * Callback function that is called when connection to the Electrum server is lost.
1129
- * Aborts all active requests with an error message indicating that connection was lost.
1130
- *
1131
- * @ignore
1132
- */ onConnectionDisconnect() {
1133
- // Emit a disconnection signal to any listeners.
1134
- this.emit("disconnected");
1135
- // Loop over active requests
1136
- for(const resolverId in this.requestResolvers){
1137
- // Extract request resolver for readability
1138
- const requestResolver = this.requestResolvers[resolverId];
1139
- // Resolve the active request with an error indicating that the connection was lost.
1140
- requestResolver(new Error("Connection lost"));
1141
- // Remove the promise from the request list.
1142
- delete this.requestResolvers[resolverId];
1143
- }
1144
- }
1145
- }
1146
- var // Export the client.
1147
- $ab5ca7f40897fe20$export$2e2bcd8739ae039 = $ab5ca7f40897fe20$var$ElectrumClient;
1148
-
1149
-
1150
-
1151
-
1152
-
1153
-
1154
-
1155
-
1156
-
1157
- /**
1158
- * Triggers when the cluster connects to enough servers to satisfy both the cluster confidence and distribution policies.
1159
- *
1160
- * @event ElectrumCluster#ready
1161
- * @deprecated
1162
- */ /**
1163
- * Triggers when the cluster loses a connection and can no longer satisfy the cluster distribution policy.
1164
- *
1165
- * @event ElectrumCluster#degraded
1166
- * @deprecated
1167
- */ /**
1168
- * Triggers when the cluster loses a connection and can no longer satisfy the cluster confidence policy.
1169
- *
1170
- * @event ElectrumCluster#disabled
1171
- * @deprecated
1172
- */ /**
1173
- * Triggers when the cluster verifies the integrity of remote server sent data that is not a request responses.
1174
- *
1175
- * @event ElectrumCluster#notification
1176
- * @deprecated
1177
- */ /**
1178
- * High-level electrum client that provides transparent load balancing, confidence checking and/or low-latency polling.
1179
- * @deprecated
1180
- */ class $683dbcc250bd73d7$var$ElectrumCluster extends (0, $790lB$events.EventEmitter) {
1181
- /**
1182
- * @param {string} application your application name, used to identify to the electrum hosts.
1183
- * @param {string} version protocol version to use with the hosts.
1184
- * @param {number} confidence wait for this number of hosts to provide identical results.
1185
- * @param {number} distribution request information from this number of hosts.
1186
- * @param {ClusterOrder} order select hosts to communicate with in this order.
1187
- * @param {number} timeout how long network delays we will wait for before taking action, in milliseconds.
1188
- * @param {number} pingInterval the time between sending pings to the electrum host, in milliseconds.
1189
- * @param {number} reconnectInterval the time between reconnection attempts to the electrum host, in milliseconds.
1190
- * @param {boolean} useBigInt whether to use bigint for numbers in json response.
1191
- */ constructor(application, version, confidence = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).CLUSTER_CONFIDENCE, distribution = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).CLUSTER_DISTRIBUTION, order = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).CLUSTER_ORDER, timeout = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).TIMEOUT, pingInterval = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).PING_INTERVAL, reconnectInterval = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).RECONNECT, useBigInt = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).USE_BIG_INT){
1192
- // Initialize the event emitter.
1193
- super();
1194
- this.application = application;
1195
- this.version = version;
1196
- this.confidence = confidence;
1197
- this.distribution = distribution;
1198
- this.order = order;
1199
- this.timeout = timeout;
1200
- this.pingInterval = pingInterval;
1201
- this.reconnectInterval = reconnectInterval;
1202
- this.useBigInt = useBigInt;
1203
- this.// Initialize an empty dictionary of clients in the cluster
1204
- clients = {};
1205
- this.// Start at 0 connected clients
1206
- connections = 0;
1207
- this.// Set up an empty set of notification data.
1208
- notifications = {};
1209
- this.// Start the cluster in DISABLED state
1210
- status = (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).DISABLED;
1211
- this.// Start counting request IDs at 0
1212
- requestCounter = 0;
1213
- this.// Initialize an empty dictionary for keeping track of request resolvers
1214
- requestPromises = {};
1215
- this.// Lock to prevent concurrency race conditions when sending requests.
1216
- requestLock = new (0, $790lB$asyncmutex.Mutex)();
1217
- this.// Lock to prevent concurrency race conditions when receiving responses.
1218
- responseLock = new (0, $790lB$asyncmutex.Mutex)();
1219
- // Write a log message.
1220
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster(`Initialized empty cluster (${confidence} of ${distribution || "ALL"})`);
1221
- // Print out a warning if we cannot guarantee consensus for subscription notifications.
1222
- // Case 1: we don't know how many servers will be used, so warning just to be safe
1223
- // Case 2: we know the number of servers needed to trust a response is less than 50%.
1224
- if (distribution === (0, $09f19ceb374f83a1$export$436a960acc41e848).ALL || confidence / distribution <= 0.50) (0, $8677acc278daa2d1$export$2e2bcd8739ae039).warning(`Subscriptions might return multiple valid responses when confidence (${confidence}) is less than 51% of distribution.`);
1225
- }
1226
- /**
1227
- * Adds a server to the cluster.
1228
- * @deprecated
1229
- *
1230
- * @param {string} host fully qualified domain name or IP number of the host.
1231
- * @param {number} port the TCP network port of the host.
1232
- * @param {TransportScheme} scheme the transport scheme to use for connection
1233
- * @param {boolean} autoConnect flag indicating whether the server should automatically connect (default true)
1234
- *
1235
- * @throws {Error} if the cluster's version is not a valid version string.
1236
- * @returns a promise that resolves when the connection has been initiated.
1237
- */ async addServer(host, port = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).PORT, scheme = (0, $48ce8effa4fcde4d$export$f019be48b3aacb1a).TRANSPORT_SCHEME, autoConnect = true) {
1238
- // Set up a new electrum client.
1239
- const client = new (0, $ab5ca7f40897fe20$export$2e2bcd8739ae039)(this.application, this.version, host, port, scheme, this.timeout, this.pingInterval, this.reconnectInterval, this.useBigInt);
1240
- // Define the client identity to avoid repetition.
1241
- const clientIdentity = `${host}:${port}`;
1242
- // Store this client.
1243
- this.clients[clientIdentity] = {
1244
- state: (0, $09f19ceb374f83a1$export$c4f81c6d30ca200f).UNAVAILABLE,
1245
- connection: client
1246
- };
1247
- /**
1248
- * Define a helper function to evaluate and log cluster status.
1249
- *
1250
- * @fires ElectrumCluster#ready
1251
- * @fires ElectrumCluster#degraded
1252
- * @fires ElectrumCluster#disabled
1253
- */ const updateClusterStatus = ()=>{
1254
- // Calculate the required distribution, taking into account that distribution to all is represented with 0.
1255
- const distribution = Math.max(this.confidence, this.distribution);
1256
- // Check if we have enough connections to saturate distribution.
1257
- if (this.connections >= distribution) // If the cluster is not currently considered ready..
1258
- {
1259
- if (this.status !== (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).READY) {
1260
- // Mark the cluster as ready.
1261
- this.status = (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).READY;
1262
- // Emit the ready signal to indicate the cluster is running in a ready mode.
1263
- this.emit("ready");
1264
- // Write a log message with an update on the current cluster status.
1265
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster(`Cluster status is ready (currently ${this.connections} of ${distribution} connections available.)`);
1266
- }
1267
- } else if (this.connections >= this.confidence) // If the cluster is not currently considered degraded..
1268
- {
1269
- if (this.status !== (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).DEGRADED) {
1270
- // Mark the cluster as degraded.
1271
- this.status = (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).DEGRADED;
1272
- // Emit the degraded signal to indicate the cluster is running in a degraded mode.
1273
- this.emit("degraded");
1274
- // Write a log message with an update on the current cluster status.
1275
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster(`Cluster status is degraded (only ${this.connections} of ${distribution} connections available.)`);
1276
- }
1277
- } else if (this.status !== (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).DISABLED) {
1278
- // Mark the cluster as disabled.
1279
- this.status = (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).DISABLED;
1280
- // Emit the degraded signal to indicate the cluster is disabled.
1281
- this.emit("disabled");
1282
- // Write a log message with an update on the current cluster status.
1283
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster(`Cluster status is disabled (only ${this.connections} of the ${distribution} connections are available.)`);
1284
- }
1285
- };
1286
- // Define a function to run when client has connected.
1287
- const onConnect = async ()=>{
1288
- // Wrap in a try-catch so we can ignore errors.
1289
- try {
1290
- // Check connection status
1291
- const connectionStatus = client.connection.status;
1292
- // If the connection is fine..
1293
- if (connectionStatus === (0, $09f19ceb374f83a1$export$7516420eb880ab68).CONNECTED) {
1294
- // If this was from an unavailable connection..
1295
- if (this.clients[clientIdentity].state === (0, $09f19ceb374f83a1$export$c4f81c6d30ca200f).UNAVAILABLE) // Update connection counter.
1296
- this.connections += 1;
1297
- // Set client state to available.
1298
- this.clients[clientIdentity].state = (0, $09f19ceb374f83a1$export$c4f81c6d30ca200f).AVAILABLE;
1299
- // update the cluster status.
1300
- updateClusterStatus();
1301
- }
1302
- } catch (error) {
1303
- // Do nothing.
1304
- }
1305
- };
1306
- // Define a function to run when client disconnects.
1307
- const onDisconnect = ()=>{
1308
- // If this was from an established connection..
1309
- if (this.clients[clientIdentity].state === (0, $09f19ceb374f83a1$export$c4f81c6d30ca200f).AVAILABLE) // Update connection counter.
1310
- this.connections -= 1;
1311
- // Set client state to unavailable.
1312
- this.clients[clientIdentity].state = (0, $09f19ceb374f83a1$export$c4f81c6d30ca200f).UNAVAILABLE;
1313
- // update the cluster status.
1314
- updateClusterStatus();
1315
- };
1316
- // Set up handlers for connection and disconnection.
1317
- client.connection.on("connect", onConnect.bind(this));
1318
- client.connection.on("disconnect", onDisconnect.bind(this));
1319
- // Set up handler for notification events, that includes the identity of this client so it can be tracked.
1320
- client.on("notification", this.handleSubscriptionNotifications.bind(this, clientIdentity));
1321
- // Connect if auto-connect is set to true, returning the connection result.
1322
- if (autoConnect) try {
1323
- // Set up the connection.
1324
- await client.connect();
1325
- } catch (error) {
1326
- // Log a message why the connection failed and move on.
1327
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster(`Failed to connect with ${host}: ${error}`);
1328
- }
1329
- }
1330
- /**
1331
- * Calls a method on the remote server with the supplied parameters.
1332
- * @deprecated
1333
- *
1334
- * @param {string} method name of the method to call.
1335
- * @param {...string} parameters one or more parameters for the method.
1336
- *
1337
- * @throws {Error} if not enough clients are connected
1338
- * @throws {Error} if no response is received with sufficient integrity
1339
- * @returns a promise that resolves with the result of the method.
1340
- */ async request(method, ...parameters) {
1341
- // Check if the cluster is unable to serve requests.
1342
- if (this.status === (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).DISABLED) throw new Error(`Cannot request '${method}' when available clients (${this.connections}) is less than required confidence (${this.confidence}).`);
1343
- // Lock this request method temporarily.
1344
- const unlock = await this.requestLock.acquire();
1345
- // Declare requestId outside of try-catch scope.
1346
- let requestId = 0;
1347
- // NOTE: If this async method is called very rapidly, it's theoretically possible that the parts below could interfere.
1348
- try {
1349
- // Increase the current request counter.
1350
- this.requestCounter += 1;
1351
- // Copy the request counter so we can work with the copy and know it won't change
1352
- // even if the request counter is raised from concurrent requests.
1353
- requestId = this.requestCounter;
1354
- } finally{
1355
- // Unlock this request method now that the concurrency sensitive condition is completed.
1356
- unlock();
1357
- }
1358
- // Initialize an empty list of request promises.
1359
- this.requestPromises[requestId] = [];
1360
- // Extract all available client IDs
1361
- const availableClientIDs = Object.keys(this.clients).filter((clientID)=>this.clients[clientID].state === (0, $09f19ceb374f83a1$export$c4f81c6d30ca200f).AVAILABLE);
1362
- // Initialize a sent counter.
1363
- let sentCounter = 0;
1364
- // Determine the number of clients we need to send to, taking ClusterDistribution.ALL (=0) into account.
1365
- let requiredDistribution = this.distribution || availableClientIDs.length;
1366
- // If the cluster is in degraded status, we do not have enough available clients to
1367
- // match distribution, but still enough to reach consensus, so we use the clients we have.
1368
- if (this.status === (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).DEGRADED) requiredDistribution = availableClientIDs.length;
1369
- // Repeat until we have sent the request to the desired number of clients.
1370
- while(sentCounter < requiredDistribution){
1371
- // Pick an array index according to our ordering strategy.
1372
- let currentIndex = 0;
1373
- // Use a random array index when cluster order is set to RANDOM
1374
- if (this.order === (0, $09f19ceb374f83a1$export$161fe3707f756bf9).RANDOM) currentIndex = Math.floor(Math.random() * availableClientIDs.length);
1375
- // Move a client identity from the client list to its own variable.
1376
- const [currentClient] = availableClientIDs.splice(currentIndex, 1);
1377
- // Send the request to the client and store the request promise.
1378
- const requestPromise = this.clients[currentClient].connection.request(method, ...parameters);
1379
- this.requestPromises[requestId].push(requestPromise);
1380
- // Increase the sent counter.
1381
- sentCounter += 1;
1382
- }
1383
- // Define a function to poll for request responses.
1384
- const pollResponse = (resolve, reject)=>{
1385
- // Define a function to resolve request responses based on integrity.
1386
- const resolveRequest = async ()=>{
1387
- // Set up an empty set of response data.
1388
- const responseData = {};
1389
- // Set up a counter to keep track of how many responses we have checked.
1390
- let checkedResponses = 0;
1391
- // For each server we issued a request to..
1392
- for(const currentPromise in this.requestPromises[requestId]){
1393
- // Initialize a holder for the response in the required scope to use it.
1394
- let response;
1395
- // Race the request promise against a pre-resolved request to determine request status.
1396
- try {
1397
- // Arrange an array of the current promise and an empty promise such that..
1398
- const promises = [
1399
- this.requestPromises[requestId][currentPromise],
1400
- Promise.resolve(undefined)
1401
- ];
1402
- // .. we can get the result of the current promise if it is currently resolved, but don't need to wait for it otherwise.
1403
- response = await Promise.race(promises);
1404
- } // Handle case where the request sent resulted in a thrown error / promise rejection, rather then resolving to a response.
1405
- // Note that in the worst time case, each request can be expected to eventually throw an error on timeout.
1406
- catch (error) {
1407
- // Increase the counter for checked responses.
1408
- checkedResponses += 1;
1409
- continue;
1410
- }
1411
- // If the promise is settled..
1412
- if (response !== undefined) {
1413
- // Calculate a unique identifier for this notification data.
1414
- const responseDataIdentifier = (0, $790lB$losslessjson.stringify)(response);
1415
- // Increase the counter for checked responses.
1416
- checkedResponses += 1;
1417
- // Either set the response data counter or increase it.
1418
- if (responseData[responseDataIdentifier] === undefined) responseData[responseDataIdentifier] = 1;
1419
- else responseData[responseDataIdentifier] += 1;
1420
- // Check if this response has enough integrity according to our confidence strategy.
1421
- if (responseData[responseDataIdentifier] === this.confidence) {
1422
- // Write log entry.
1423
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster(`Validated response for '${method}' with sufficient integrity (${this.confidence}).`);
1424
- // Resolve the request with this response.
1425
- resolve(response);
1426
- // Return after resolving since we do not want to continue the execution.
1427
- return;
1428
- }
1429
- }
1430
- }
1431
- // If all clients have responded but we failed to reach desired integrity..
1432
- if (checkedResponses === this.requestPromises[requestId].length) {
1433
- // Reject this request with an error message.
1434
- reject(new Error(`Unable to complete request for '${method}', response failed to reach sufficient integrity (${this.confidence}).`));
1435
- // Return after rejecting since we do not want to continue the execution.
1436
- return;
1437
- }
1438
- // If we are not ready, but have not timed out and should wait more..
1439
- setTimeout(resolveRequest, 1000);
1440
- };
1441
- // Attempt the initial resolution of the request.
1442
- resolveRequest();
1443
- };
1444
- // return some kind of promise that resolves when integrity number of clients results match.
1445
- return new Promise(pollResponse);
1446
- }
1447
- /**
1448
- * Subscribes to the method at the cluster and attaches the callback function to the event feed.
1449
- * @deprecated
1450
- *
1451
- * @note the response for the subscription request is issued as a notification event.
1452
- *
1453
- * @param {string} method one of the subscribable methods the server supports.
1454
- * @param {...string} parameters one or more parameters for the method.
1455
- *
1456
- * @throws {Error} if not enough clients are connected
1457
- * @throws {Error} if no response is received with sufficient integrity for the initial request
1458
- */ async subscribe(method, ...parameters) {
1459
- // Set up event listener for this subscription.
1460
- for(const currentClient in this.clients){
1461
- // Copy the current client for brevity.
1462
- const client = this.clients[currentClient].connection;
1463
- try {
1464
- // Send initial subscription request.
1465
- // NOTE: This stores and manages the subscription even if the initial request fails.
1466
- await client.subscribe(method, ...parameters);
1467
- } catch (error) {
1468
- // Do nothing, as this is handled on a best-effort basis and
1469
- // not all servers are expected to be ready at all times.
1470
- }
1471
- }
1472
- }
1473
- /**
1474
- * Unsubscribes to the method at the cluster and removes any callback functions
1475
- * when there are no more subscriptions for the method.
1476
- * @deprecated
1477
- *
1478
- * @param {string} method one of the subscribable methods the server supports.
1479
- * @param {...string} parameters one or more parameters for the method.
1480
- *
1481
- * @throws {Error} if, for any of the clients, no subscriptions exist for the combination of the provided `method` and `parameters.
1482
- */ async unsubscribe(method, ...parameters) {
1483
- // Initialize an empty list to track subscription requests.
1484
- const unsubscriptionPromises = [];
1485
- // For each client..
1486
- for(const currentClient in this.clients){
1487
- // Store client in variable for brevity
1488
- const client = this.clients[currentClient].connection;
1489
- // unsubscribe this client.
1490
- unsubscriptionPromises.push(client.unsubscribe(method, ...parameters));
1491
- }
1492
- // Wait for all unsubscription promises to resolve.
1493
- await Promise.all(unsubscriptionPromises);
1494
- }
1495
- /**
1496
- * Define a callback function to validate server notifications and pass them to the subscribe callback.
1497
- * @deprecated
1498
- *
1499
- * @ignore
1500
- */ async handleSubscriptionNotifications(clientIdentity, data) {
1501
- // Lock this response method temporarily.
1502
- const unlock = await this.responseLock.acquire();
1503
- try {
1504
- // Calculate a unique identifier for this notification data.
1505
- const responseDataIdentifier = (0, $790lB$losslessjson.stringify)(data);
1506
- // Create an empty list of clients who have responded to this notification, if necessary.
1507
- if (this.notifications[responseDataIdentifier] === undefined) this.notifications[responseDataIdentifier] = new Set();
1508
- // Ensure this client is on the list of clients that have provided this specific notification.
1509
- this.notifications[responseDataIdentifier].add(clientIdentity);
1510
- // Check if this notification has enough integrity according to our confidence strategy.
1511
- // NOTE: We check against === instead of >== in order to ensure that we only emit each notification once.
1512
- if (this.notifications[responseDataIdentifier].size === this.confidence) {
1513
- // Write log entry.
1514
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster(`Validated notification for '${data.method}' with sufficient integrity (${this.confidence}).`);
1515
- // Emit an event for the notification data.
1516
- this.emit("notification", data);
1517
- // Dismiss the notification data after all nodes are assumed to have sent their notifications.
1518
- // NOTE: This is a redundant mechanic to ensure that even if some nodes don't provide this notification, we still clear this data.
1519
- // NOTE: This also introduces a race-condition where if a legit identical notification comes in before/during this timeout, it might get silenced.
1520
- setTimeout(this.dismissSubscriptionNotification.bind(this, responseDataIdentifier), this.timeout);
1521
- }
1522
- // Check if this notification has been fully handled.
1523
- if (this.notifications[responseDataIdentifier].size === this.distribution) // Dismiss existing response data as we know all related parties have provided their input.
1524
- this.dismissSubscriptionNotification(responseDataIdentifier);
1525
- } finally{
1526
- // Unlock the response method so it can handle the next set of data.
1527
- unlock();
1528
- }
1529
- }
1530
- /**
1531
- * Forgets/Removes notification data for a specific notification.
1532
- *
1533
- * This is required in order to allow future identical notifications to be properly processed and emitted.
1534
- * @deprecated
1535
- */ async dismissSubscriptionNotification(responseDataIdentifier) {
1536
- delete this.notifications[responseDataIdentifier];
1537
- }
1538
- /**
1539
- * Provides a method to check or wait for the cluster to become ready.
1540
- * @deprecated
1541
- *
1542
- * @returns a promise that resolves when the required servers are available.
1543
- */ async ready() {
1544
- // Store the current timestamp.
1545
- const readyTimestamp = Date.now();
1546
- // Define a function to poll for availability of the cluster.
1547
- const availabilityPoller = (resolve)=>{
1548
- // Define a function to check if the cluster is ready to be used.
1549
- const connectionAvailabilityVerifier = ()=>{
1550
- // Check if the cluster is active..
1551
- if (this.status === (0, $09f19ceb374f83a1$export$c66b56bc0ff967ca).READY) {
1552
- // Resolve with true to indicate that the cluster is ready to use.
1553
- resolve(true);
1554
- // Return after resolving since we do not want to continue the execution.
1555
- return;
1556
- }
1557
- // Calculate how long we have waited, in milliseconds.
1558
- const timeWaited = Date.now() - readyTimestamp;
1559
- // Check if we have waited longer than our timeout setting.
1560
- if (timeWaited > this.timeout) {
1561
- // Resolve with false to indicate that we did not get ready in time.
1562
- resolve(false);
1563
- // Return after resolving since we do not want to continue the execution.
1564
- return;
1565
- }
1566
- // If we are not ready, but have not timed out and should wait more..
1567
- setTimeout(connectionAvailabilityVerifier, 50);
1568
- };
1569
- // Run the initial verification.
1570
- connectionAvailabilityVerifier();
1571
- };
1572
- // Return a promise that resolves when the available clients is sufficient.
1573
- return new Promise(availabilityPoller);
1574
- }
1575
- /**
1576
- * Connects all servers from the cluster and attaches event listeners and handlers
1577
- * for all underlying clients and connections.
1578
- * @deprecated
1579
- *
1580
- * @throws {Error} if the cluster's version is not a valid version string.
1581
- */ async startup() {
1582
- // Write a log message.
1583
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster("Starting up cluster.");
1584
- // Keep track of all connections
1585
- const connections = [];
1586
- // Loop over all clients and reconnect them if they're disconnected
1587
- for(const clientKey in this.clients){
1588
- // Retrieve connection information for the client
1589
- const { host: host, port: port, scheme: scheme } = this.clients[clientKey].connection.connection;
1590
- // Only connect currently unavailable/disconnected clients
1591
- if (this.clients[clientKey].state === (0, $09f19ceb374f83a1$export$c4f81c6d30ca200f).AVAILABLE) // Warn when a server is already connected when calling startup()
1592
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).warning(`Called startup(), but server ${host}:${port} is already connected`);
1593
- else // Call the addServer() function with the existing connection data
1594
- // This effectively reconnects the server and re-instates all event listeners
1595
- connections.push(this.addServer(host, port, scheme));
1596
- }
1597
- // Await all connections
1598
- return Promise.all(connections);
1599
- }
1600
- /**
1601
- * Disconnects all servers from the cluster. Removes all event listeners and
1602
- * handlers from all underlying clients and connections. This includes all
1603
- * active subscriptions, unless retainSubscriptions is set to true.
1604
- * @deprecated
1605
- *
1606
- * @param {boolean} retainSubscriptions retain subscription data so they will be restored on reconnection.
1607
- *
1608
- * @returns a list with the disconnection result for every client
1609
- */ async shutdown(retainSubscriptions = false) {
1610
- // Write a log message.
1611
- (0, $8677acc278daa2d1$export$2e2bcd8739ae039).cluster("Shutting down cluster.");
1612
- // Set up a list of disconnections to wait for.
1613
- const disconnections = [];
1614
- const disconnectResolver = (resolve)=>{
1615
- // Resolve once the cluster is marked as disabled
1616
- this.once("disabled", ()=>resolve(Promise.all(disconnections)));
1617
- // For each client in this cluster..
1618
- for(const clientIndex in this.clients)// Force disconnection regardless of current status.
1619
- disconnections.push(this.clients[clientIndex].connection.disconnect(true, retainSubscriptions));
1620
- };
1621
- // Return a list of booleans indicating disconnections from all clients
1622
- return new Promise(disconnectResolver);
1623
- }
1624
- }
1625
- var // Export the cluster.
1626
- $683dbcc250bd73d7$export$2e2bcd8739ae039 = $683dbcc250bd73d7$var$ElectrumCluster;
1627
-
1628
-
1629
-
1630
-
1631
-
1632
- $parcel$exportWildcard(module.exports, $5fc020d2a13224ff$exports);
1633
- $parcel$exportWildcard(module.exports, $48ce8effa4fcde4d$exports);
1634
- $parcel$exportWildcard(module.exports, $09f19ceb374f83a1$exports);
1635
-
1636
-
1637
- //# sourceMappingURL=index.cjs.map