xud
Version:
Exchange Union Daemon
266 lines (265 loc) • 11.8 kB
TypeScript
/// <reference types="node" />
import { EventEmitter } from 'events';
import { Socket } from 'net';
import { DisconnectionReason, ReputationEvent, SwapClientType } from '../constants/enums';
import Logger from '../Logger';
import NodeKey from '../nodekey/NodeKey';
import { OutgoingOrder } from '../orderbook/types';
import Network from './Network';
import { Packet } from './packets';
import { ResponseType } from './packets/Packet';
import * as packets from './packets/types';
import { Address, NodeConnectionInfo, NodeState } from './types';
/** Key info about a peer for display purposes */
declare type PeerInfo = {
address: string;
nodePubKey?: string;
alias?: string;
inbound: boolean;
pairs?: string[];
xudVersion?: string;
secondsConnected: number;
lndPubKeys?: {
[currency: string]: string | undefined;
};
connextIdentifier?: string;
};
interface Peer {
on(event: 'packet', listener: (packet: Packet) => void): this;
on(event: 'reputation', listener: (event: ReputationEvent) => void): this;
/** Adds a listener to be called when the peer's advertised but inactive pairs should be verified. */
on(event: 'verifyPairs', listener: () => void): this;
/** Adds a listener to be called when a previously active pair is dropped by the peer or deactivated. */
on(event: 'pairDropped', listener: (pairId: string) => void): this;
on(event: 'nodeStateUpdate', listener: () => void): this;
once(event: 'close', listener: () => void): this;
emit(event: 'connect'): boolean;
emit(event: 'reputation', reputationEvent: ReputationEvent): boolean;
emit(event: 'close'): boolean;
emit(event: 'packet', packet: Packet): boolean;
/** Notifies listeners that the peer's advertised but inactive pairs should be verified. */
emit(event: 'verifyPairs'): boolean;
/** Notifies listeners that a previously active pair was dropped by the peer or deactivated. */
emit(event: 'pairDropped', pairId: string): boolean;
emit(event: 'nodeStateUpdate'): boolean;
}
/** Represents a remote peer and manages a TCP socket and incoming/outgoing communication with that peer. */
declare class Peer extends EventEmitter {
private logger;
address: Address;
inbound: boolean;
/** The reason this peer disconnected from us. */
recvDisconnectionReason?: DisconnectionReason;
/** The reason we told this peer we disconnected from it. */
sentDisconnectionReason?: DisconnectionReason;
expectedNodePubKey?: string;
/** Whether the peer is included in the p2p pool list of peers and will receive broadcasted packets. */
active: boolean;
/** Timer to periodically call getNodes #402 */
discoverTimer?: NodeJS.Timer;
/**
* Currencies that we cannot swap because we are missing a swap client identifier or because the
* peer's token identifier for this currency does not match ours - for example this may happen
* because a peer is using a different token contract address for a currency than we are.
*/
disabledCurrencies: Set<string>;
/** Interval to check required responses from peer. */
static readonly STALL_INTERVAL = 5000;
private status;
/** Trading pairs advertised by this peer which we have verified that we can swap. */
private activePairs;
/** Currencies that we have verified we can swap with this peer. */
private activeCurrencies;
private socket?;
private readonly parser;
/** Timer to retry connection to peer after the previous attempt failed. */
private retryConnectionTimer?;
private stallTimer?;
private pingTimer?;
private checkPairsTimer?;
private readonly responseMap;
/** The epoch time in ms when we connected to this peer. */
private connectTime;
private connectionRetriesRevoked;
/** The version of xud this peer is using. */
private _version?;
/** The node pub key of this peer. */
private _nodePubKey?;
private _alias?;
private nodeState?;
private sessionInitPacket?;
private outEncryptionKey?;
private readonly network;
private readonly framer;
/** Interval for pinging peers. */
private static readonly PING_INTERVAL;
/** Interval for checking if we can reactivate any inactive pairs with peers. */
private static readonly CHECK_PAIRS_INTERVAL;
/** Response timeout for response packets. */
private static readonly RESPONSE_TIMEOUT;
/** Connection retries min delay. */
private static readonly CONNECTION_RETRIES_MIN_DELAY;
/** Connection retries max delay. */
private static readonly CONNECTION_RETRIES_MAX_DELAY;
/** Connection retries max period. */
private static readonly CONNECTION_RETRIES_MAX_PERIOD;
/** The version of xud this peer is using, or an empty string if it is still not known. */
get version(): string;
/** The hex-encoded node public key for this peer, or undefined if it is still not known. */
get nodePubKey(): string | undefined;
set nodePubKey(nodePubKey: string | undefined);
get alias(): string | undefined;
get label(): string;
get addresses(): Address[] | undefined;
get connextIdentifier(): string | undefined;
/** Returns a list of trading pairs advertised by this peer. */
get advertisedPairs(): string[];
get connected(): boolean;
get info(): PeerInfo;
/**
* @param address The socket address for the connection to this peer.
*/
constructor(logger: Logger, address: Address, network: Network);
/**
* Creates a Peer from an inbound socket connection.
*/
static fromInbound: (socket: Socket, logger: Logger, network: Network) => Peer;
getAdvertisedCurrencies: () => Set<string>;
getLndPubKey(currency?: string): string | undefined;
getIdentifier: (clientType: SwapClientType, currency?: string | undefined) => string | undefined;
getTokenIdentifier: (currency: string) => string | undefined;
getStatus: () => string;
/**
* Prepares a peer for use by establishing a socket connection and beginning the handshake.
* @returns the session init packet from beginning the handshake
*/
beginOpen: ({ ownNodeState, ownNodeKey, ownVersion, expectedNodePubKey, retryConnecting, torport, }: {
/** Our node state data to send to the peer. */
ownNodeState: NodeState;
/** Our identity node key. */
ownNodeKey: NodeKey;
/** The version of xud we are running. */
ownVersion: string;
/** The expected nodePubKey of the node we are opening a connection with. */
expectedNodePubKey?: string | undefined;
/** Whether to retry to connect upon failure. */
retryConnecting?: boolean | undefined;
/** Port that Tor's exposed SOCKS5 proxy is listening on. */
torport: number;
}) => Promise<packets.SessionInitPacket>;
/**
* Finishes opening a peer for use by marking the peer as opened, completing the handshake,
* and setting up the ping packet timer.
* @param ownNodeState our node state data to send to the peer
* @param ownNodeKey our identity node key
* @param ownVersion the version of xud we are running
* @param sessionInit the session init packet we received when beginning the handshake
*/
completeOpen: (ownNodeState: NodeState, ownNodeKey: NodeKey, ownVersion: string, sessionInit: packets.SessionInitPacket) => Promise<void>;
/**
* Close a peer by ensuring the socket is destroyed and terminating all timers.
*/
close: (reason?: DisconnectionReason | undefined, reasonPayload?: string | undefined) => Promise<void>;
revokeConnectionRetries: () => void;
sendPacket: (packet: Packet) => Promise<void>;
sendOrders: (orders: OutgoingOrder[], reqId: string) => Promise<void>;
/** Sends a [[NodesPacket]] containing node connection info to this peer. */
sendNodes: (nodes: NodeConnectionInfo[], reqId: string) => Promise<void>;
deactivateCurrency: (currency: string) => void;
activateCurrency: (currency: string) => Set<string>;
disableCurrency: (currency: string) => void;
enableCurrency: (currency: string) => void;
/**
* Deactivates a trading pair with this peer.
*/
deactivatePair: (pairId: string) => void;
/**
* Activates a trading pair with this peer.
*/
activatePair: (pairId: string) => Promise<void>;
isPairActive: (pairId: string) => boolean;
isCurrencyActive: (currency: string) => boolean;
/**
* Gets lnd client's listening uris for the provided currency.
* @param currency
*/
getLndUris(currency: string): string[] | undefined;
/**
* Ensure we are connected (for inbound connections) or listen for the `connect` socket event (for outbound connections)
* and set the [[connectTime]] timestamp. If an outbound connection attempt errors or times out, throw an error.
*/
private initConnection;
private initStall;
/**
* Waits for a packet to be received from peer.
* @returns A promise that is resolved once the packet is received or rejects on timeout.
*/
wait: (reqId: string, resType: ResponseType, timeout?: number | undefined, cb?: ((packet: Packet) => void) | undefined) => Promise<Packet>;
private waitSessionInit;
/**
* Potentially timeout peer if it hasn't responded.
*/
private checkTimeout;
/**
* Wait for a packet to be received from peer.
*/
private addResponseTimeout;
private getOrAddPendingResponseEntry;
/**
* Fulfill a pending response entry for solicited responses, penalize unsolicited responses.
* @returns false if no pending response entry exists for the provided key, otherwise true
*/
private fulfillResponseEntry;
/**
* Binds listeners to a newly connected socket for `error`, `close`, and `data` events.
*/
private bindSocket;
private bindParser;
/** Checks if a given packet is solicited and fulfills the pending response entry if it's a response. */
private isPacketSolicited;
private handlePacket;
/**
* Authenticates the identity of a peer with a [[SessionInitPacket]] and sets the peer's node state.
* Throws an error and closes the peer if authentication fails.
* @param packet the session init packet
* @param nodePubKey our node pub key
* @param expectedNodePubKey the expected node pub key of the sender of the init packet
*/
private authenticateSessionInit;
/**
* Sets public key and alias for this node together so that they are always in sync.
*/
setIdentifiers(nodePubKey: string): void;
/**
* Sends a [[SessionInitPacket]] and waits for a [[SessionAckPacket]].
*/
private initSession;
/**
* Sends a [[SessionAckPacket]] in response to a given [[SessionInitPacket]].
*/
private ackSession;
/**
* Begins the handshake by waiting for a [[SessionInitPacket]] as well as sending our own
* [[SessionInitPacket]] first if we are the outbound peer.
* @returns the session init packet we receive
*/
private beginHandshake;
/**
* Completes the handshake by sending the [[SessionAckPacket]] and our [[SessionInitPacket]] if it
* has not been sent already, as is the case with inbound peers.
*/
private completeHandshake;
private sendPing;
sendGetNodes: () => Promise<packets.GetNodesPacket>;
discoverNodes: () => Promise<number>;
private sendPong;
private handlePing;
private createSessionInitPacket;
private handleDisconnecting;
private handleSessionInit;
private handleNodeStateUpdate;
private setOutEncryption;
private setInEncryption;
}
export default Peer;
export { PeerInfo };