Skip to content

Commit

Permalink
feat(swaps): get peer identifier for swaps
Browse files Browse the repository at this point in the history
This replaces the `Peer` `getLndPubKey` method with a generic
`getIdentifier` method that will return either an lnd pubkey or a
Raiden address.
  • Loading branch information
sangaman committed Apr 2, 2019
1 parent f6279cd commit fd8c17f
Show file tree
Hide file tree
Showing 15 changed files with 71 additions and 63 deletions.
2 changes: 2 additions & 0 deletions lib/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Logger from './Logger';
import { EventEmitter } from 'events';
import { SwapDeal } from './swaps/types';
import { Route } from './proto/lndrpc_pb';
import { SwapClient } from './constants/enums';

enum ClientStatus {
NotInitialized,
Expand All @@ -21,6 +22,7 @@ type ChannelBalance = {
*/
abstract class BaseClient extends EventEmitter {
public abstract readonly cltvDelta: number;
public abstract readonly type: SwapClient;
public maximumOutboundCapacity = 0;
protected status: ClientStatus = ClientStatus.NotInitialized;
protected reconnectionTimer?: NodeJS.Timer;
Expand Down
4 changes: 2 additions & 2 deletions lib/cli/commands/addcurrency.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Arguments } from 'yargs';
import { callback, loadXudClient } from '../command';
import { AddCurrencyRequest } from '../../proto/xudrpc_pb';
import { SwapClients } from '../../constants/enums';
import { SwapClient } from '../../constants/enums';

export const command = 'addcurrency <currency> <swap_client> [decimal_places] [token_address]';

Expand Down Expand Up @@ -31,7 +31,7 @@ export const builder = {
export const handler = (argv: Arguments) => {
const request = new AddCurrencyRequest();
request.setCurrency(argv.currency.toUpperCase());
request.setSwapClient(Number(SwapClients[argv.swap_client]));
request.setSwapClient(Number(SwapClient[argv.swap_client]));
request.setTokenAddress(argv.token_address);
request.setDecimalPlaces(argv.decimal_places);
loadXudClient(argv).addCurrency(request, callback(argv));
Expand Down
10 changes: 5 additions & 5 deletions lib/constants/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** An enumeration of payment channel network clients that support token swaps. */
export enum SwapClients {
export enum SwapClient {
Lnd,
Raiden,
}
Expand Down Expand Up @@ -87,13 +87,13 @@ export enum SwapFailureReason {
SwapClientNotSetup = 3,
/** Could not find a route to complete the swap. */
NoRouteFound = 4,
/** A call to lnd failed for an unexpected reason. */
UnexpectedLndError = 5,
/** A swap client call failed for an unexpected reason. */
UnexpectedClientError = 5,
/** Received a swap packet from the peer with invalid data. */
InvalidSwapPacketReceived = 6,
/** The call to lnd to send payment failed. */
/** The call to send payment failed. */
SendPaymentFailure = 7,
/** The swap resolver request from lnd was invalid. */
/** The swap resolver request was invalid. */
InvalidResolveRequest = 8,
/** The swap request attempts to reuse a payment hash. */
PaymentHashReuse = 9,
Expand Down
6 changes: 3 additions & 3 deletions lib/db/DB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Sequelize from 'sequelize';
import Bluebird from 'bluebird';
import Logger from '../Logger';
import * as db from './types';
import { SwapClients } from '../constants/enums';
import { SwapClient } from '../constants/enums';
import { exists, readdir } from '../utils/fsUtils';

type Models = {
Expand Down Expand Up @@ -87,8 +87,8 @@ class DB {
]));

promises.push(Currency.bulkCreate(<db.CurrencyAttributes[]>[
{ id: 'BTC', swapClient: SwapClients.Lnd, decimalPlaces: 8 },
{ id: 'LTC', swapClient: SwapClients.Lnd, decimalPlaces: 8 },
{ id: 'BTC', swapClient: SwapClient.Lnd, decimalPlaces: 8 },
{ id: 'LTC', swapClient: SwapClient.Lnd, decimalPlaces: 8 },
// { id: 'ZRX', swapClient: SwapClients.Raiden, decimalPlaces: 18 },
// { id: 'GNT', swapClient: SwapClients.Raiden, decimalPlaces: 18 },
]));
Expand Down
2 changes: 1 addition & 1 deletion lib/grpc/GrpcService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ class GrpcService {
case SwapFailureReason.OrderOnHold:
code = status.FAILED_PRECONDITION;
break;
case SwapFailureReason.UnexpectedLndError:
case SwapFailureReason.UnexpectedClientError:
case SwapFailureReason.UnknownError:
default:
code = status.UNKNOWN;
Expand Down
3 changes: 2 additions & 1 deletion lib/lndclient/LndClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { LightningClient } from '../proto/lndrpc_grpc_pb';
import * as lndrpc from '../proto/lndrpc_pb';
import assert from 'assert';
import { exists, readFile } from '../utils/fsUtils';
import { SwapState, SwapRole } from '../constants/enums';
import { SwapState, SwapRole, SwapClient } from '../constants/enums';
import { SwapDeal } from '../swaps/types';

/** The configurable options for the lnd client. */
Expand Down Expand Up @@ -48,6 +48,7 @@ interface LndClient {

/** A class representing a client to interact with lnd. */
class LndClient extends BaseClient {
public readonly type = SwapClient.Lnd;
public readonly cltvDelta: number;
private lightning!: LightningClient | LightningMethodIndex;
private meta!: grpc.Metadata;
Expand Down
4 changes: 2 additions & 2 deletions lib/orderbook/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SwapClients } from '../constants/enums';
import { SwapClient } from '../constants/enums';
import { SwapSuccess, SwapFailure } from 'lib/swaps/types';

export type OrderMatch = {
Expand Down Expand Up @@ -100,7 +100,7 @@ export type Currency = {
/** The ticker symbol for this currency such as BTC, LTC, ETH, etc... */
id: string;
/** The payment channel network client to use for executing swaps. */
swapClient: SwapClients;
swapClient: SwapClient;
/**
* The number of places to the right of the decimal point of the smallest subunit of the currency, For example, BTC, LTC, and others
* where the smallest subunits (satoshis) are 0.00000001 full units (bitcoins) have 8 decimal places. ETH has 18. This can be thought
Expand Down
14 changes: 10 additions & 4 deletions lib/p2p/Peer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { EventEmitter } from 'events';
import crypto from 'crypto';
import secp256k1 from 'secp256k1';
import stringify from 'json-stable-stringify';
import { ReputationEvent, DisconnectionReason, NetworkMagic } from '../constants/enums';
import { ReputationEvent, DisconnectionReason, NetworkMagic, SwapClient } from '../constants/enums';
import Parser from './Parser';
import * as packets from './packets/types';
import Logger from '../Logger';
Expand Down Expand Up @@ -161,11 +161,17 @@ class Peer extends EventEmitter {
return peer;
}

public getLndPubKey(currency: string): string | undefined {
if (!this.nodeState || !this.nodeState.lndPubKeys) {
public getIdentifier(clientType: SwapClient, currency?: string): string | undefined {
if (!this.nodeState) {
return undefined;
}
return this.nodeState.lndPubKeys[currency];
if (clientType === SwapClient.Lnd && currency) {
return this.nodeState.lndPubKeys[currency];
}
if (clientType === SwapClient.Raiden) {
return this.nodeState.raidenAddress;
}
return;
}

public getStatus = (): string => {
Expand Down
5 changes: 3 additions & 2 deletions lib/raidenclient/RaidenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import http from 'http';
import Logger from '../Logger';
import BaseClient, { ClientStatus, ChannelBalance } from '../BaseClient';
import errors from './errors';
import { ms } from '../utils/utils';
import { Order } from '../orderbook/types';
import { SwapDeal } from 'lib/swaps/types';
import { SwapDeal } from '../swaps/types';
import { SwapClient } from '../constants/enums';

/**
* A utility function to parse the payload from an http response.
Expand Down Expand Up @@ -78,6 +78,7 @@ interface RaidenClient {
* A class representing a client to interact with raiden.
*/
class RaidenClient extends BaseClient {
public readonly type = SwapClient.Raiden;
public readonly cltvDelta: number = 0;
public address?: string;
private port: number;
Expand Down
6 changes: 3 additions & 3 deletions lib/service/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import RaidenClient, { RaidenInfo } from '../raidenclient/RaidenClient';
import Client from '../BaseClient';
import { EventEmitter } from 'events';
import errors from './errors';
import { SwapClients, OrderSide, SwapRole } from '../constants/enums';
import { SwapClient, OrderSide, SwapRole } from '../constants/enums';
import { parseUri, toUri, UriParts } from '../utils/uriUtils';
import * as lndrpc from '../proto/lndrpc_pb';
import { Pair, Order, OrderPortion, PlaceOrderEvent } from '../orderbook/types';
Expand Down Expand Up @@ -59,7 +59,7 @@ const argChecks = {
if (port < 1024 || port > 65535 || !Number.isInteger(port)) throw errors.INVALID_ARGUMENT('port must be an integer between 1024 and 65535');
},
VALID_SWAP_CLIENT: ({ swapClient }: { swapClient: number }) => {
if (!SwapClients[swapClient]) throw errors.INVALID_ARGUMENT('swap client is not recognized');
if (!SwapClient[swapClient]) throw errors.INVALID_ARGUMENT('swap client is not recognized');
},
};

Expand Down Expand Up @@ -88,7 +88,7 @@ class Service extends EventEmitter {
}

/** Adds a currency. */
public addCurrency = async (args: { currency: string, swapClient: SwapClients | number, decimalPlaces: number, tokenAddress?: string}) => {
public addCurrency = async (args: { currency: string, swapClient: SwapClient | number, decimalPlaces: number, tokenAddress?: string}) => {
argChecks.VALID_CURRENCY(args);
argChecks.VALID_SWAP_CLIENT(args);
const { currency, swapClient, tokenAddress, decimalPlaces } = args;
Expand Down
50 changes: 24 additions & 26 deletions lib/swaps/Swaps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SwapPhase, SwapRole, SwapState, SwapFailureReason, ReputationEvent } from '../constants/enums';
import { SwapPhase, SwapRole, SwapState, SwapFailureReason, ReputationEvent, SwapClient } from '../constants/enums';
import Peer from '../p2p/Peer';
import { Models } from '../db/DB';
import * as packets from '../p2p/packets/types';
Expand Down Expand Up @@ -131,16 +131,16 @@ class Swaps extends EventEmitter {
}

/**
* Makes sure the peer has provided pub keys for the networks involved in the swap.
* Makes sure the peer has provided identifiers for the networks involved in the swap.
* @returns undefined if the setup is verified, otherwise an error message
*/
private verifyPeerLndPubKeys = (deal: SwapDeal, peer: Peer) => {
// TODO: this verification should happen before we accept orders from the peer
if (!peer.getLndPubKey(deal.takerCurrency)) {
private checkPeerIdentifiers = (deal: SwapDeal, peer: Peer) => {
// TODO: this verification should happen before we accept orders from the peer and should check Raiden as well
if (!peer.getIdentifier(SwapClient.Lnd, deal.takerCurrency)) {
return 'peer did not provide an LND PubKey for ' + deal.takerCurrency;
}

if (!peer.getLndPubKey(deal.makerCurrency)) {
if (!peer.getIdentifier(SwapClient.Lnd, deal.makerCurrency)) {
return 'peer did not provide an LND PubKey for ' + deal.makerCurrency;
}
return;
Expand Down Expand Up @@ -196,7 +196,7 @@ class Swaps extends EventEmitter {
if (!swapClient) throw new Error('swap client not found');

const peer = this.pool.getPeer(maker.peerPubKey);
const destination = peer.getLndPubKey(makerCurrency);
const destination = peer.getIdentifier(swapClient.type, makerCurrency);
if (!destination) {
throw new Error(`${makerCurrency} client's pubKey not found for peer ${maker.peerPubKey}`);
}
Expand All @@ -205,7 +205,7 @@ class Swaps extends EventEmitter {
try {
routes = await swapClient.getRoutes(makerAmount, destination);
} catch (err) {
throw SwapFailureReason.UnexpectedLndError;
throw SwapFailureReason.UnexpectedClientError;
}

if (routes.length === 0) {
Expand Down Expand Up @@ -258,7 +258,8 @@ class Swaps extends EventEmitter {

const quantity = Math.min(maker.quantity, taker.quantity);
const { makerAmount, takerAmount } = Swaps.calculateSwapAmounts(quantity, maker.price, maker.isBuy);
const destination = peer.getLndPubKey(makerCurrency)!;
const clientType = this.swapClients[makerCurrency]!.type;
const destination = peer.getIdentifier(clientType, makerCurrency)!;

const takerCltvDelta = this.swapClients[takerCurrency]!.cltvDelta;

Expand Down Expand Up @@ -297,7 +298,7 @@ class Swaps extends EventEmitter {
this.addDeal(deal);

// Verify LND setup. Make sure the peer has given us its lnd pub keys.
const errMsg = this.verifyPeerLndPubKeys(deal, peer);
const errMsg = this.checkPeerIdentifiers(deal, peer);
if (errMsg) {
this.logger.error(errMsg);
this.failDeal(deal, SwapFailureReason.SwapClientNotSetup, errMsg);
Expand Down Expand Up @@ -331,9 +332,13 @@ class Swaps extends EventEmitter {
const { makerAmount, takerAmount } = Swaps.calculateSwapAmounts(quantity, price, isBuy);
const { makerCurrency, takerCurrency } = Swaps.deriveCurrencies(requestBody.pairId, isBuy);

const takerPubKey = peer.getLndPubKey(takerCurrency)!;
const swapClient = this.swapClients[takerCurrency];
if (!swapClient) {
await this.sendErrorToPeer(peer, rHash, SwapFailureReason.SwapClientNotSetup, 'Unsupported taker currency', requestPacket.header.id);
return false;
}

const destination = peer.getLndPubKey(makerCurrency)!;
const takerPubKey = peer.getIdentifier(swapClient.type, takerCurrency)!;

const deal: SwapDeal = {
...requestBody,
Expand All @@ -345,7 +350,7 @@ class Swaps extends EventEmitter {
takerAmount,
makerCurrency,
takerCurrency,
destination,
destination: takerPubKey,
peerPubKey: peer.nodePubKey!,
localId: orderToAccept.localId,
phase: SwapPhase.SwapCreated,
Expand All @@ -361,27 +366,20 @@ class Swaps extends EventEmitter {
this.addDeal(deal);

// Verify LND setup. Make sure the peer has given us its lnd pub keys.
const errMsg = this.verifyPeerLndPubKeys(deal, peer);
const errMsg = this.checkPeerIdentifiers(deal, peer);
if (errMsg) {
this.failDeal(deal, SwapFailureReason.SwapClientNotSetup, errMsg);
await this.sendErrorToPeer(peer, deal.rHash, deal.failureReason!, deal.errorMessage, requestPacket.header.id);
return false;
}
// Make sure we are connected to lnd for both currencies
// Make sure we are connected to swap clients for both currencies
if (!this.isPairSupported(deal.pairId)) {
this.failDeal(deal, SwapFailureReason.SwapClientNotSetup, errMsg);
await this.sendErrorToPeer(peer, deal.rHash, deal.failureReason!, deal.errorMessage, requestPacket.header.id);
return false;
}

// TODO: verify that a route exists before accepting deal

const swapClient = this.swapClients[deal.takerCurrency];
if (!swapClient) {
this.failDeal(deal, SwapFailureReason.SwapClientNotSetup, 'Unsupported taker currency');
await this.sendErrorToPeer(peer, deal.rHash, deal.failureReason!, deal.errorMessage, requestPacket.header.id);
return false;
}
// TODO: verify that a route exists from taker to maker before accepting deal

try {
deal.makerToTakerRoutes = await swapClient.getRoutes(takerAmount, deal.peerPubKey);
Expand All @@ -391,7 +389,7 @@ class Swaps extends EventEmitter {
return false;
}
} catch (err) {
this.failDeal(deal, SwapFailureReason.UnexpectedLndError, err.message);
this.failDeal(deal, SwapFailureReason.UnexpectedClientError, err.message);
await this.sendErrorToPeer(peer, deal.rHash, deal.failureReason!, deal.errorMessage, requestPacket.header.id);
return false;
}
Expand All @@ -400,7 +398,7 @@ class Swaps extends EventEmitter {
try {
height = await swapClient.getHeight();
} catch (err) {
this.failDeal(deal, SwapFailureReason.UnexpectedLndError, 'Unable to fetch block height: ' + err.message);
this.failDeal(deal, SwapFailureReason.UnexpectedClientError, 'Unable to fetch block height: ' + err.message);
await this.sendErrorToPeer(peer, deal.rHash, deal.failureReason!, deal.errorMessage, requestPacket.header.id);
return false;
}
Expand All @@ -413,7 +411,7 @@ class Swaps extends EventEmitter {
const makerClientCltvDelta = this.swapClients[makerCurrency]!.cltvDelta;
const takerClientCltvDelta = this.swapClients[takerCurrency]!.cltvDelta;

// cltvDelta can't be zero for lnd clients (checked in constructor)
// cltvDelta can't be zero for swap clients (checked in constructor)
const cltvDeltaFactor = makerClientCltvDelta / takerClientCltvDelta;

deal.makerCltvDelta = makerClientCltvDelta + Math.ceil(routeCltvDelta * cltvDeltaFactor);
Expand Down
6 changes: 3 additions & 3 deletions test/integration/OrderBook.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import OrderBook from '../../lib/orderbook/OrderBook';
import OrderBookRepository from '../../lib/orderbook/OrderBookRepository';
import Logger, { Level } from '../../lib/Logger';
import * as orders from '../../lib/orderbook/types';
import { SwapClients } from '../../lib/constants/enums';
import { SwapClient } from '../../lib/constants/enums';
import { createOwnOrder } from '../utils';

const PAIR_ID = 'LTC/BTC';
Expand All @@ -16,8 +16,8 @@ const initValues = async (db: DB) => {
const orderBookRepository = new OrderBookRepository(loggers.orderbook, db.models);

await orderBookRepository.addCurrencies([
{ id: currencies[0], swapClient: SwapClients.Lnd, decimalPlaces: 8 },
{ id: currencies[1], swapClient: SwapClients.Lnd, decimalPlaces: 8 },
{ id: currencies[0], swapClient: SwapClient.Lnd, decimalPlaces: 8 },
{ id: currencies[1], swapClient: SwapClient.Lnd, decimalPlaces: 8 },
]);
await orderBookRepository.addPairs([
{ baseCurrency: currencies[0], quoteCurrency: currencies[1] },
Expand Down
Loading

0 comments on commit fd8c17f

Please sign in to comment.