xud
Version:
Exchange Union Daemon
227 lines (226 loc) • 11.2 kB
TypeScript
/// <reference types="node" />
import { EventEmitter } from 'events';
import { DisconnectionReason, ReputationEvent, XuNetwork } from '../constants/enums';
import { Models } from '../db/DB';
import Logger from '../Logger';
import NodeKey from '../nodekey/NodeKey';
import { IncomingOrder, OrderInvalidation, OrderPortion, OutgoingOrder } from '../orderbook/types';
import { Packet } from './packets';
import * as packets from './packets/types';
import Peer, { PeerInfo } from './Peer';
import { Address, PoolConfig } from './types';
declare type NodeReputationInfo = {
reputationScore: ReputationEvent;
banned?: boolean;
};
interface Pool {
on(event: 'packet.order', listener: (order: IncomingOrder) => void): this;
on(event: 'packet.getOrders', listener: (peer: Peer, reqId: string, pairIds: string[]) => void): this;
on(event: 'packet.orderInvalidation', listener: (orderInvalidation: OrderInvalidation, peer: string) => void): this;
on(event: 'peer.active', listener: (peerPubKey: string) => void): this;
on(event: 'peer.close', listener: (peerPubKey?: string) => void): this;
/** Adds a listener to be called when a peer's advertised but inactive pairs should be verified. */
on(event: 'peer.verifyPairs', listener: (peer: Peer) => void): this;
/** Adds a listener to be called when a previously active pair is dropped by the peer or deactivated. */
on(event: 'peer.pairDropped', listener: (peerPubKey: string, pairId: string) => void): this;
on(event: 'peer.nodeStateUpdate', listener: (peer: Peer) => void): this;
on(event: 'packet.sanitySwapInit', listener: (packet: packets.SanitySwapInitPacket, peer: Peer) => void): this;
on(event: 'packet.swapRequest', listener: (packet: packets.SwapRequestPacket, peer: Peer) => void): this;
on(event: 'packet.swapAccepted', listener: (packet: packets.SwapAcceptedPacket, peer: Peer) => void): this;
on(event: 'packet.swapFailed', listener: (packet: packets.SwapFailedPacket) => void): this;
emit(event: 'packet.order', order: IncomingOrder): boolean;
emit(event: 'packet.getOrders', peer: Peer, reqId: string, pairIds: string[]): boolean;
emit(event: 'packet.orderInvalidation', orderInvalidation: OrderInvalidation, peer: string): boolean;
emit(event: 'peer.active', peerPubKey: string): boolean;
emit(event: 'peer.close', peerPubKey?: string): boolean;
/** Notifies listeners that a peer's advertised but inactive pairs should be verified. */
emit(event: 'peer.verifyPairs', peer: Peer): boolean;
/** Notifies listeners that a previously active pair was dropped by the peer or deactivated. */
emit(event: 'peer.pairDropped', peerPubKey: string, pairId: string): boolean;
emit(event: 'peer.nodeStateUpdate', peer: Peer): boolean;
emit(event: 'packet.sanitySwapInit', packet: packets.SanitySwapInitPacket, peer: Peer): boolean;
emit(event: 'packet.swapRequest', packet: packets.SwapRequestPacket, peer: Peer): boolean;
emit(event: 'packet.swapAccepted', packet: packets.SwapAcceptedPacket, peer: Peer): boolean;
emit(event: 'packet.swapFailed', packet: packets.SwapFailedPacket): boolean;
}
/**
* Represents a pool of peers that handles all p2p network activity. This tracks all active and
* pending peers, optionally runs a server to listen for incoming connections, and is the primary
* interface for other modules to interact with the p2p layer.
*/
declare class Pool extends EventEmitter {
/** The version of xud we are using. */
version: string;
/** Our node pub key. */
nodePubKey: string;
/** Our alias. */
alias: string;
/** The local handshake data to be sent to newly connected peers. */
private nodeState;
/** A map of pub keys to nodes for which we have pending outgoing connections. */
private pendingOutboundPeers;
/** A set of peers for which we have pending incoming connections. */
private pendingInboundPeers;
/** A collection of known nodes on the XU network. */
private nodes;
private loadingNodesPromise?;
/** A collection of opened, active peers. */
private peers;
private server?;
private disconnecting;
private connected;
/** The port on which to listen for peer connections, undefined if this node is not listening. */
private listenPort?;
/** Points to config comes during construction. */
private config;
private strict;
private repository;
private network;
private logger;
private nodeKey;
/** The minimum version of xud we accept for peers */
private minCompatibleVersion;
constructor({ config, xuNetwork, logger, models, nodeKey, version, strict, minCompatibleVersion }: {
config: PoolConfig;
xuNetwork: XuNetwork;
logger: Logger;
models: Models;
nodeKey: NodeKey;
version: string;
strict?: boolean;
minCompatibleVersion?: string;
});
get peerCount(): number;
get addresses(): Address[];
getTokenIdentifier: (currency: string) => string | undefined;
getNodePubKeyById: (nodeId: number) => string | undefined;
getNodeId: (nodePubKey: string) => number | undefined;
getNodeAlias: (nodePubKey: string) => string | undefined;
/**
* Initialize the Pool by connecting to known nodes and listening to incoming peer connections, if configured to do so.
*/
init: () => Promise<void>;
private detectExternalIpAddress;
/**
* Updates our active trading pairs and sends a node state update packet to currently connected
* peers to notify them of the change.
*/
updatePairs: (pairIds: string[]) => void;
/**
* Updates our connext public key and supported token addresses, then sends a node state update
* packet to currently connected peers to notify them of the change.
*/
updateConnextState: (tokenAddresses: Map<string, string>, pubKey?: string | undefined) => void;
/**
* Updates our lnd pub key and chain identifier for a given currency and sends a node state
* update packet to currently connected peers to notify them of the change.
*/
updateLndState: ({ currency, pubKey, chain, uris }: {
currency: string;
pubKey: string;
chain?: string | undefined;
uris?: string[] | undefined;
}) => void;
private sendNodeStateUpdate;
disconnect: () => Promise<void>;
private bindNodeList;
private verifyReachability;
/**
* Iterate over a collection of nodes and attempt to connect to them.
* If the node is banned, already connected, or has no listening addresses, then do nothing.
* Additionally, if we're already trying to connect to a given node also do nothing.
* @param nodes a collection of nodes with a `forEach` iterator to attempt to connect to
* @param allowKnown whether to allow connecting to nodes we are already aware of, defaults to true
* @param retryConnecting whether to attempt retry connecting, defaults to false
* @returns a promise that will resolve when all outbound connections resolve
*/
private connectNodes;
/**
* Attempt to create an outbound connection to a node using its known listening addresses.
*/
private tryConnectNode;
private tryConnectWithLastAddress;
private tryConnectWithAdvertisedAddresses;
/**
* Gets the active XU network as specified by the configuration.
*
* @returns the active XU network
*/
getNetwork: () => XuNetwork;
/**
* Gets a node's reputation score and whether it is banned
* @param nodePubKey The node pub key of the node for which to get reputation information
* @return true if the specified node exists and the event was added, false otherwise
*/
getNodeReputation: (nodePubKey: string) => Promise<NodeReputationInfo>;
/**
* Attempt to add an outbound peer by connecting to a given socket address and nodePubKey.
* Throws an error if a socket connection to or the handshake with the node fails for any reason.
* @param address the socket address of the node to connect to
* @param nodePubKey the nodePubKey of the node to connect to
* @returns a promise that resolves to the connected and opened peer
*/
addOutbound: (address: Address, nodePubKey: string, retryConnecting: boolean, revokeConnectionRetries: boolean) => Promise<Peer>;
listPeers: () => PeerInfo[];
rawPeers: () => Map<string, Peer>;
private addressIsSelf;
private tryOpenPeer;
/**
* Opens a connection to a peer and performs a routine for newly opened peers that includes
* requesting open orders and updating the database with the peer's information.
* @returns a promise that resolves once the connection has opened and the newly opened peer
* routine is complete
*/
private openPeer;
private handleOpenedPeer;
closePeer: (nodePubKey: string, reason?: DisconnectionReason | undefined, reasonPayload?: string | undefined) => Promise<void>;
banNode: (nodePubKey: string) => Promise<void>;
unbanNode: (nodePubKey: string, reconnect: boolean) => Promise<void>;
discoverNodes: (peerPubKey: string) => Promise<number>;
addReputationEvent: (nodePubKey: string, event: ReputationEvent) => Promise<void>;
sendToPeer: (nodePubKey: string, packet: Packet) => Promise<void>;
/**
* Gets a peer by its node pub key or alias. Throws a [[NOT_CONNECTED]] error if the supplied identifier does not
* match any currently connected peer.
*/
getPeer: (peerPubKey: string) => Peer;
tryGetPeer: (peerPubKey: string) => Peer | undefined;
broadcastOrder: (order: OutgoingOrder) => void;
/**
* Broadcasts an [[OrderInvalidationPacket]] to all currently connected peers.
* @param nodeToExclude the node pub key of a node to exclude from the packet broadcast
*/
broadcastOrderInvalidation: ({ id, pairId, quantity }: OrderPortion, nodeToExclude?: string | undefined) => void;
private addInbound;
private handleSocket;
private handlePacket;
/** Validates a peer. If a check fails, closes the peer and throws a p2p error. */
private validatePeer;
/**
* Responds to a [[GetNodesPacket]] by populating and sending a [[NodesPacket]].
*/
private handleGetNodes;
private bindServer;
private bindPeer;
private handlePeerClose;
private closePeers;
private closePendingConnections;
/**
* Starts listening for incoming p2p connections on the configured host and port. If `this.listenPort` is 0 or undefined,
* a random available port is used and will be assigned to `this.listenPort`.
* @return a promise that resolves once the server is listening, or rejects if it fails to listen
*/
private listen;
/**
* Stops listening for incoming p2p connections.
* @return a promise that resolves once the server is no longer listening
*/
private unlisten;
/**
* Resolves an alias to a known node's public key. Throws an error if a unique
* pub key cannot be found for the provided alias.
*/
resolveAlias: (alias: string) => string;
}
export default Pool;
export { PoolConfig };