import * as net from "net"; import { WebSocket, MessageEvent, ErrorEvent } from "@monsterbitar/isomorphic-ws"; import { EventEmitter } from "events"; import { Mutex } from "async-mutex"; type RPCParameter = string | number | boolean | null; interface RPCBase { jsonrpc: string; } interface RPCNotification extends RPCBase { method: string; params?: RPCParameter[]; } interface RPCStatement extends RPCBase { id: number | null; result: string; } interface RPCError { code: number; message: string; data?: any; } interface RPCErrorResponse extends RPCBase { id: number | null; error: RPCError; } type RPCResponse = RPCErrorResponse | RPCStatement; /** * Enum that denotes the ordering to use in an ElectrumCluster. * @enum {number} * @property {0} RANDOM Send requests to randomly selected servers in the cluster. * @property {1} PRIORITY Send requests to servers in the cluster in the order they were added. */ export enum ClusterOrder { RANDOM = 0, PRIORITY = 1 } /** * Enum that denotes the distribution setting to use in an ElectrumCluster. * @enum {number} * @property {0} ALL Send requests to all servers in the cluster. */ export enum ClusterDistribution { ALL = 0 } /** * Enum that denotes the ready status of an ElectrumCluster. * @enum {number} * @property {0} DISABLED The cluster is disabled and unusable. * @property {1} DEGRADED The cluster is degraded but still usable. * @property {2} READY The cluster is healthy and ready for use. */ export enum ClusterStatus { DISABLED = 0, DEGRADED = 1, READY = 2 } /** * Enum that denotes the availability of an ElectrumClient. * @enum {number} * @property {0} UNAVAILABLE The client is currently not available. * @property {1} AVAILABLE The client is available for use. */ export enum ClientState { UNAVAILABLE = 0, AVAILABLE = 1 } /** * Enum that denotes the connection status of an ElectrumConnection. * @enum {number} * @property {0} DISCONNECTED The connection is disconnected. * @property {1} AVAILABLE The connection is connected. * @property {2} DISCONNECTING The connection is disconnecting. * @property {3} CONNECTING The connection is connecting. * @property {4} RECONNECTING The connection is restarting. */ export enum ConnectionStatus { DISCONNECTED = 0, CONNECTED = 1, DISCONNECTING = 2, CONNECTING = 3, RECONNECTING = 4 } export interface ClientConfig { state: ClientState; connection: ElectrumClient; } /** * A list of possible responses to requests. */ export type RequestResponse = object | string | number | boolean | null | RequestResponse[]; export type RequestResolver = (error?: Error, data?: string) => void; export type ResolveFunction = (value: T | PromiseLike) => void; export type RejectFunction = (reason?: any) => void; export interface VersionRejected { error: RPCError; } export interface VersionNegotiated { software: string; protocol: string; } export const isVersionRejected: (object: any) => object is VersionRejected; export const isVersionNegotiated: (object: any) => object is VersionNegotiated; /** * Possible Transport Schemes for communication with the Electrum server */ export type TransportScheme = 'tcp' | 'tcp_tls' | 'ws' | 'wss'; export interface ConnectionOptions { rejectUnauthorized?: boolean; serverName?: string; } /** * Object containing the commonly used ports and schemes for specific Transports. * @example const electrum = new ElectrumClient('Electrum client example', '1.4.1', 'bch.imaginary.cash', Transport.WSS.Port, Transport.WSS.Scheme); * * @property {object} TCP Port and Scheme to use unencrypted TCP sockets. * @property {object} TCP_TLS Port and Scheme to use TLS-encrypted TCP sockets. * @property {object} WS Port and Scheme to use unencrypted WebSockets. * @property {object} WSS Port and Scheme to use TLS-encrypted WebSockets. */ export const ElectrumTransport: { TCP: { Port: number; Scheme: TransportScheme; }; TCP_TLS: { Port: number; Scheme: TransportScheme; }; WS: { Port: number; Scheme: TransportScheme; }; WSS: { Port: number; Scheme: TransportScheme; }; }; export const DefaultParameters: { PORT: number; TRANSPORT_SCHEME: TransportScheme; RECONNECT: number; TIMEOUT: number; PING_INTERVAL: number; CLUSTER_CONFIDENCE: number; CLUSTER_DISTRIBUTION: ClusterDistribution; CLUSTER_ORDER: ClusterOrder; }; /** * Isomorphic Socket interface supporting TCP sockets and WebSockets (Node and browser). * The interface is a subset of the TLSSocket interface with some slight modifications. * It can be expanded when more socket functionality is needed in the rest of the * electrum-cash code. Changes from the TLSSocket interface (besides it being a subset): * - Event 'close' -> 'disconnect' * - New function socket.disconnect() * * @ignore */ declare class ElectrumSocket extends EventEmitter { tcpSocket?: net.Socket; webSocket?: WebSocket; timers: { retryConnection?: NodeJS.Timer; disconnect?: NodeJS.Timer; }; onConnectHasRun: boolean; eventForwarders: { disconnect: () => boolean; tcpData: (data: string) => boolean; wsData: (event: MessageEvent) => boolean; tcpError: (err: Error) => boolean; wsError: (event: ErrorEvent) => boolean; }; /** * Connect to host:port using the specified transport * * @param {string} host Fully qualified domain name or IP address of the host * @param {number} port Network port for the host to connect to * @param {TransportScheme} scheme Transport scheme to use * @param {number} timeout If no connection is established after `timeout` ms, the connection is terminated * * @throws {Error} if an incorrect transport scheme is specified */ connect(host: string, port: number, scheme: TransportScheme, timeout: number): void; /** * Sets up forwarding of events related to the connection. * * @param {string} connectionType Name of the connection/transport type, used for logging. * @param {string} host Fully qualified domain name or IP address of the host * @param {number} port Network port for the host to connect to */ onConnect(connectionType: string, host: string, port: number): void; /** * Forcibly terminate the connection. * * @throws {Error} if no connection was found */ disconnect(): void; /** * Write data to the socket * * @param {Uint8Array | string} data Data to be written to the socket * @param {function} callback Callback function to be called when the write has completed * * @throws {Error} if no connection was found * @returns true if the message was fully flushed to the socket, false if part of the message * is queued in the user memory */ write(data: Uint8Array | string, callback?: (err?: Error) => void): boolean; /** * Force a disconnection if no connection is established after `timeout` milliseconds. * * @param {string} host Host of the connection that timed out * @param {number} port Port of the connection that timed out * @param {number} timeout Elapsed milliseconds */ disconnectOnTimeout(host: string, port: number, timeout: number): void; } /** * Wrapper around TLS/WSS sockets that gracefully separates a network stream into Electrum protocol messages. * * @ignore */ declare class ElectrumConnection extends EventEmitter { application: string; version: string; host: string; port: number; scheme: TransportScheme; timeout: number; pingInterval: number; reconnectInterval: number; socket: ElectrumSocket; lastReceivedTimestamp: number; timers: { keepAlive?: NodeJS.Timer; reconnect?: NodeJS.Timer; }; verifications: Array; status: ConnectionStatus; messageBuffer: string; /** * Sets up network configuration for an Electrum client connection. * * @param {string} application your application name, used to identify to the electrum host. * @param {string} version protocol version to use with the host. * @param {string} host fully qualified domain name or IP number of the host. * @param {number} port the network port of the host. * @param {TransportScheme} scheme the transport scheme to use for connection * @param {number} timeout how long network delays we will wait for before taking action, in milliseconds. * @param {number} pingInterval the time between sending pings to the electrum host, in milliseconds. * @param {number} reconnectInterval the time between reconnection attempts to the electrum host, in milliseconds. * * @throws {Error} if `version` is not a valid version string. */ constructor(application: string, version: string, host: string, port?: number, scheme?: TransportScheme, timeout?: number, pingInterval?: number, reconnectInterval?: number); /** * Returns a string for the host identifier for usage in debug messages. */ get hostIdentifier(): string; /** * Create and configures a fresh socket and attaches all relevant listeners. */ createSocket(): void; /** * Shuts down and destroys the current socket. */ destroySocket(): void; /** * Assembles incoming data into statements and hands them off to the message parser. * * @param {string} data data to append to the current message buffer, as a string. * * @throws {SyntaxError} if the passed statement parts are not valid JSON. */ parseMessageChunk(data: string): void; /** * Sends a keep-alive message to the host. * * @returns true if the ping message was fully flushed to the socket, false if * part of the message is queued in the user memory */ ping(): boolean; /** * Initiates the network connection negotiates a protocol version. Also emits the 'connect' signal if successful. * * @throws {Error} if the socket connection fails. * @returns a promise resolving when the connection is established */ connect(): Promise; /** * Restores the network connection. */ reconnect(): Promise; /** * Removes the current reconnect timer. */ clearReconnectTimer(): void; /** * Removes the current keep-alive timer. */ clearKeepAliveTimer(): void; /** * Initializes the keep alive timer loop. */ setupKeepAliveTimer(): void; /** * Tears down the current connection and removes all event listeners on disconnect. * * @param {boolean} force disconnect even if the connection has not been fully established yet. * @param {boolean} intentional update connection state if disconnect is intentional. * * @returns true if successfully disconnected, or false if there was no connection. */ disconnect(force?: boolean, intentional?: boolean): Promise; /** * Updates connection state based on application visibility. * * Some browsers will disconnect network connections when the browser is out of focus, * which would normally cause our reconnect-on-timeout routines to trigger, but that * results in a poor user experience since the events are not handled consistently * and sometimes it can take some time after restoring focus to the browser. * * By manually disconnecting when this happens we prevent the default reconnection routines * and make the behavior consistent across browsers. */ handleVisibilityChange(): Promise; /** * Sends an arbitrary message to the server. * * @param {string} message json encoded request object to send to the server, as a string. * * @returns true if the message was fully flushed to the socket, false if part of the message * is queued in the user memory */ send(message: string): boolean; /** * Marks the connection as timed out and schedules reconnection if we have not * received data within the expected time frame. */ verifySend(sentTimestamp: number): void; /** * Updates the connection status when a connection is confirmed. */ onSocketConnect(): void; /** * Updates the connection status when a connection is ended. */ onSocketDisconnect(): void; /** * Notify administrator of any unexpected errors. */ onSocketError(error: any | undefined): void; } /** * Triggers when the underlying connection is established. * * @event ElectrumClient#connected */ /** * Triggers when the underlying connection is lost. * * @event ElectrumClient#disconnected */ /** * Triggers when the remote server sends data that is not a request response. * * @event ElectrumClient#notification */ /** * High-level Electrum client that lets applications send requests and subscribe to notification events from a server. */ export class ElectrumClient extends EventEmitter { connection: ElectrumConnection; subscriptionMethods: Record>; requestId: number; requestResolvers: { [index: number]: RequestResolver; }; connectionLock: Mutex; /** * Initializes an Electrum client. * * @param {string} application your application name, used to identify to the electrum host. * @param {string} version protocol version to use with the host. * @param {string} host fully qualified domain name or IP number of the host. * @param {number} port the TCP network port of the host. * @param {TransportScheme} scheme the transport scheme to use for connection * @param {number} timeout how long network delays we will wait for before taking action, in milliseconds. * @param {number} pingInterval the time between sending pings to the electrum host, in milliseconds. * @param {number} reconnectInterval the time between reconnection attempts to the electrum host, in milliseconds. * * @throws {Error} if `version` is not a valid version string. */ constructor(application: string, version: string, host: string, port?: number, scheme?: TransportScheme, timeout?: number, pingInterval?: number, reconnectInterval?: number); /** * Connects to the remote server. * * @throws {Error} if the socket connection fails. * @returns a promise resolving when the connection is established. */ connect(): Promise; /** * Disconnects from the remote server and removes all event listeners/subscriptions and open requests. * * @param {boolean} force disconnect even if the connection has not been fully established yet. * @param {boolean} retainSubscriptions retain subscription data so they will be restored on reconnection. * * @returns true if successfully disconnected, or false if there was no connection. */ disconnect(force?: boolean, retainSubscriptions?: boolean): Promise; /** * Calls a method on the remote server with the supplied parameters. * * @param {string} method name of the method to call. * @param {...string} parameters one or more parameters for the method. * * @throws {Error} if the client is disconnected. * @returns a promise that resolves with the result of the method or an Error. */ request(method: string, ...parameters: RPCParameter[]): Promise; /** * Subscribes to the method and payload at the server. * * @note the response for the subscription request is issued as a notification event. * * @param {string} method one of the subscribable methods the server supports. * @param {...string} parameters one or more parameters for the method. * * @throws {Error} if the client is disconnected. * @returns a promise resolving when the subscription is established. */ subscribe(method: string, ...parameters: RPCParameter[]): Promise; /** * Unsubscribes to the method at the server and removes any callback functions * when there are no more subscriptions for the method. * * @param {string} method a previously subscribed to method. * @param {...string} parameters one or more parameters for the method. * * @throws {Error} if no subscriptions exist for the combination of the provided `method` and `parameters. * @throws {Error} if the client is disconnected. * @returns a promise resolving when the subscription is removed. */ unsubscribe(method: string, ...parameters: RPCParameter[]): Promise; /** * Parser messages from the remote server to resolve request promises and emit subscription events. * * @param {RPCNotification | RPCResponse} message the response message * * @throws {Error} if the message ID does not match an existing request. * @ignore */ response(message: RPCNotification | RPCResponse): void; /** * Callback function that is called when connection to the Electrum server is lost. * Aborts all active requests with an error message indicating that connection was lost. * * @ignore */ onConnectionDisconnect(): void; } /** * Triggers when the cluster connects to enough servers to satisfy both the cluster confidence and distribution policies. * * @event ElectrumCluster#ready */ /** * Triggers when the cluster loses a connection and can no longer satisfy the cluster distribution policy. * * @event ElectrumCluster#degraded */ /** * Triggers when the cluster loses a connection and can no longer satisfy the cluster confidence policy. * * @event ElectrumCluster#disabled */ /** * Triggers when the cluster verifies the integrity of remote server sent data that is not a request responses. * * @event ElectrumCluster#notification */ /** * High-level electrum client that provides transparent load balancing, confidence checking and/or low-latency polling. */ export class ElectrumCluster extends EventEmitter { application: string; version: string; confidence: number; distribution: number; order: ClusterOrder; timeout: number; pingInterval: number; reconnectInterval: number; clients: { [index: string]: ClientConfig; }; connections: number; notifications: Record>; status: ClusterStatus; requestCounter: number; requestPromises: { [index: number]: Promise[]; }; requestLock: Mutex; responseLock: Mutex; /** * @param {string} application your application name, used to identify to the electrum hosts. * @param {string} version protocol version to use with the hosts. * @param {number} confidence wait for this number of hosts to provide identical results. * @param {number} distribution request information from this number of hosts. * @param {ClusterOrder} order select hosts to communicate with in this order. * @param {number} timeout how long network delays we will wait for before taking action, in milliseconds. * @param {number} pingInterval the time between sending pings to the electrum host, in milliseconds. * @param {number} reconnectInterval the time between reconnection attempts to the electrum host, in milliseconds. */ constructor(application: string, version: string, confidence?: number, distribution?: number, order?: ClusterOrder, timeout?: number, pingInterval?: number, reconnectInterval?: number); /** * Adds a server to the cluster. * * @param {string} host fully qualified domain name or IP number of the host. * @param {number} port the TCP network port of the host. * @param {TransportScheme} scheme the transport scheme to use for connection * @param {boolean} autoConnect flag indicating whether the server should automatically connect (default true) * * @throws {Error} if the cluster's version is not a valid version string. * @returns a promise that resolves when the connection has been initiated. */ addServer(host: string, port?: number, scheme?: TransportScheme, autoConnect?: boolean): Promise; /** * Calls a method on the remote server with the supplied parameters. * * @param {string} method name of the method to call. * @param {...string} parameters one or more parameters for the method. * * @throws {Error} if not enough clients are connected * @throws {Error} if no response is received with sufficient integrity * @returns a promise that resolves with the result of the method. */ request(method: string, ...parameters: RPCParameter[]): Promise; /** * Subscribes to the method at the cluster and attaches the callback function to the event feed. * * @note the response for the subscription request is issued as a notification event. * * @param {string} method one of the subscribable methods the server supports. * @param {...string} parameters one or more parameters for the method. * * @throws {Error} if not enough clients are connected * @throws {Error} if no response is received with sufficient integrity for the initial request */ subscribe(method: string, ...parameters: RPCParameter[]): Promise; /** * Unsubscribes to the method at the cluster and removes any callback functions * when there are no more subscriptions for the method. * * @deprecated * * @param {string} method one of the subscribable methods the server supports. * @param {...string} parameters one or more parameters for the method. * * @throws {Error} if, for any of the clients, no subscriptions exist for the combination of the provided `method` and `parameters. */ unsubscribe(method: string, ...parameters: RPCParameter[]): Promise; /** * Define a callback function to validate server notifications and pass them to the subscribe callback. * * @ignore */ handleSubscriptionNotifications(clientIdentity: string, data: RPCNotification): Promise; /** * Forgets/Removes notification data for a specific notification. * * This is required in order to allow future identical notifications to be properly processed and emitted. */ dismissSubscriptionNotification(responseDataIdentifier: any): Promise; /** * Provides a method to check or wait for the cluster to become ready. * * @returns a promise that resolves when the required servers are available. */ ready(): Promise; /** * Connects all servers from the cluster and attaches event listeners and handlers * for all underlying clients and connections. * * @throws {Error} if the cluster's version is not a valid version string. */ startup(): Promise; /** * Disconnects all servers from the cluster. Removes all event listeners and * handlers from all underlying clients and connections. This includes all * active subscriptions, unless retainSubscriptions is set to true. * * @param {boolean} retainSubscriptions retain subscription data so they will be restored on reconnection. * * @returns a list with the disconnection result for every client */ shutdown(retainSubscriptions?: boolean): Promise; } //# sourceMappingURL=index.d.ts.map