xud
Version:
Exchange Union Daemon
191 lines (190 loc) • 9.24 kB
TypeScript
/// <reference types="node" />
import { EventEmitter } from 'events';
import { Models } from '../db/DB';
import { SwapDealInstance } from '../db/types';
import Logger from '../Logger';
import { OwnOrder, PeerOrder } from '../orderbook/types';
import * as packets from '../p2p/packets/types';
import Peer from '../p2p/Peer';
import Pool from '../p2p/Pool';
import SwapClientManager from './SwapClientManager';
import { ResolveRequest, SwapAccepted, SwapDeal, SwapSuccess } from './types';
export declare type OrderToAccept = Pick<SwapDeal, 'quantity' | 'price' | 'localId' | 'isBuy'> & {
quantity: number;
};
interface Swaps {
on(event: 'swap.accepted', listener: (swapSuccess: SwapAccepted) => void): this;
on(event: 'swap.paid', listener: (swapSuccess: SwapSuccess) => void): this;
on(event: 'swap.failed', listener: (deal: SwapDeal) => void): this;
on(event: 'swap.recovered', listener: (recoveredSwap: SwapDealInstance) => void): this;
emit(event: 'swap.accepted', swapSuccess: SwapAccepted): boolean;
emit(event: 'swap.paid', swapSuccess: SwapSuccess): boolean;
emit(event: 'swap.failed', deal: SwapDeal): boolean;
emit(event: 'swap.recovered', recoveredSwap: SwapDealInstance): boolean;
}
declare class Swaps extends EventEmitter {
swapClientManager: SwapClientManager;
/** A map between payment hashes and pending sanity swaps. */
private sanitySwaps;
private logger;
private models;
private pool;
private strict;
/** A map between payment hashes and swap deals. */
private deals;
private swapRecovery;
/** A map between payment hashes and timeouts for swaps. */
private timeouts;
private usedHashes;
private repository;
/** The maximum time in milliseconds we will wait for a swap to be accepted before failing it. */
private static readonly SWAP_ACCEPT_TIMEOUT;
/** The maximum time in milliseconds we will wait for a swap to be completed before failing it. */
private static readonly SWAP_COMPLETE_TIMEOUT;
/**
* Additional time that the maker will wait for a swap to be completed before considering it timed
* out. This exists because the maker starts timing sooner and ends timing later than the taker.
* The maker starts timing as soon as it sends its SwapAccepted packet, but taker starts upon
* receiving that packet some short time later. Furthermore, the taker stops the timer as soon as
* it reveals the preimage and settles its incoming payment, whereas the maker doesn't stop until
* it receives the preimage.
*/
private static readonly SWAP_COMPLETE_MAKER_BUFFER;
/**
* The time threshold in milliseconds after which we consider a counterparty abusive if they
* settle payment for a timed out swap.
*/
private static readonly SWAP_ABUSE_TIME_LIMIT;
/** The maximum time in milliseconds we will wait to receive an expected sanity swap init packet. */
private static readonly SANITY_SWAP_INIT_TIMEOUT;
/** The maximum time in milliseconds we will wait for a swap to be completed before failing it. */
private static readonly SANITY_SWAP_COMPLETE_TIMEOUT;
constructor({ logger, models, pool, swapClientManager, strict }: {
logger: Logger;
models: Models;
pool: Pool;
swapClientManager: SwapClientManager;
strict?: boolean;
});
/**
* Checks if a swap request is valid. This is a shallow check that only detects critical
* inconsistencies and verifies only whether the request can possibly lead to a successful swap.
* @returns `true` if the request is valid, otherwise `false`
*/
static validateSwapRequest: ({ proposedQuantity, rHash }: packets.SwapRequestPacketBody) => boolean;
/**
* Calculates the minimum expected lock delta for the final hop of the first leg to ensure a
* very high probability that it won't expire before the second leg payment. We use a Poisson
* distribution to model the possible block times of two independent chains, first calculating
* a probabilistic upper bound for the lock time in minuntes of the second leg then a
* probabilistic lower bound for the number of blocks for the lock time extended to the final
* hop of the first leg.
* @param secondLegLockDuration The lock duration (aka time lock or cltv delta) of the second
* leg (maker to taker) denominated in blocks of that chain.
* @returns A number of blocks for the chain of the first leg that is highly likely to take
* more time in minutes than the provided second leg lock duration.
*/
private static calculateLockBuffer;
/**
* Calculates the currencies and amounts of subunits/satoshis each side of a swap should receive.
* @param quantity The quantity being swapped
* @param price The price for the swap
* @param isBuy Whether the maker order in the swap is a buy
* @returns An object with the calculated maker and taker values.
*/
private static calculateMakerTakerAmounts;
init: () => Promise<void>;
private bind;
/**
* Checks if there are connected swap clients for both currencies in a given trading pair.
* @returns `undefined` if both currencies are active, otherwise the ticker symbol for an inactive currency.
*/
checkInactiveCurrencyClients: (pairId: string) => string | undefined;
/**
* Sends a swap failed packet to the counterparty peer in a swap with details about the error
* that caused the failure. Sets reqId if packet is a response to a request.
*/
private sendErrorToPeer;
/**
* Saves deal to database and deletes it from memory if it is no longer active.
* @param deal The deal to persist.
*/
private persistDeal;
getPendingSwapHashes: () => string[];
/**
* Gets a deal by its rHash value.
* @param rHash The rHash value of the deal to get.
* @returns A deal if one is found, otherwise undefined.
*/
getDeal: (rHash: string) => SwapDeal | undefined;
addDeal: (deal: SwapDeal) => void;
/**
* Checks if a swap for two given orders can be executed by ensuring both swap clients are active
* and if there exists a route to the maker.
* @param maker maker order
* @param taker taker order
* @returns `void` if the swap can be executed, throws a [[SwapFailureReason]] otherwise
*/
private verifyExecution;
/**
* A promise wrapper for a swap procedure
* @param maker the remote maker order we are filling
* @param taker our local taker order
* @returns A promise that resolves to a [[SwapSuccess]] once the swap is completed, throws a [[SwapFailureReason]] if it fails
*/
executeSwap: (maker: PeerOrder, taker: OwnOrder) => Promise<SwapSuccess>;
/**
* Executes a sanity swap with a peer for a specified currency.
* @returns `true` if the swap succeeds, otherwise `false`
*/
executeSanitySwap: (currency: string, peer: Peer) => Promise<boolean>;
/**
* Begins a swap to fill an order by sending a [[SwapRequestPacket]] to the maker.
* @param maker The remote maker order we are filling
* @param taker Our local taker order
* @returns The rHash for the swap, or a [[SwapFailureReason]] if the swap could not be initiated
*/
private beginSwap;
/**
* Accepts a proposed deal for a specified amount if a route and CLTV delta could be determined
* for the swap. Stores the deal in the local collection of deals.
* @returns A promise resolving to `true` if the deal was accepted, `false` otherwise.
*/
acceptDeal: (orderToAccept: OrderToAccept, requestPacket: packets.SwapRequestPacket, peer: Peer) => Promise<boolean>;
private handleHtlcAccepted;
/**
* Handles a response from a peer to confirm a swap deal and updates the deal. If the deal is
* accepted, initiates the swap.
*/
private handleSwapAccepted;
/**
* Verifies that the resolve request is valid. Checks the received amount vs
* the expected amount.
* @returns `true` if the resolve request is valid, `false` otherwise
*/
private validateResolveRequest;
/** Attempts to resolve the preimage for the payment hash of a pending sanity swap. */
private resolveSanitySwap;
/**
* Resolves the hash for an incoming HTLC to its preimage.
* @param rHash the payment hash to resolve
* @param amount the amount in satoshis
* @param htlcCurrency the currency of the HTLC
* @returns the preimage for the provided payment hash
*/
resolveHash: (rHash: string, amount: number, htlcCurrency?: string | undefined) => Promise<string>;
handleResolveRequest: (resolveRequest: ResolveRequest) => Promise<string>;
private handleSwapTimeout;
/**
* Fails a deal and optionally sends a SwapFailurePacket to a peer, if provided.
*/
private failDeal;
/**
* Updates the phase of a swap deal and handles logic directly related to that phase change,
* including persisting the deal state to the database.
*/
private setDealPhase;
private handleSwapFailed;
close: () => void;
}
export default Swaps;