Skip to content

Commit

Permalink
feat(rpc): failed swaps for PlaceOrder
Browse files Browse the repository at this point in the history
This adds a `SwapFailure` message type to the asynchronous `PlaceOrder`
rpc call. It is used to notify the caller in real time when a swap fails
for a given order. This also renames `SwapResult` to `SwapSuccess` to
clearly distinguish it from the unsuccessful `SwapFailure`.

Closes #609. Closes #734.
  • Loading branch information
sangaman committed Jan 23, 2019
1 parent 1aa44ad commit 6c9c2a6
Show file tree
Hide file tree
Showing 11 changed files with 965 additions and 571 deletions.
44 changes: 32 additions & 12 deletions docs/api.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/cli/commands/streamorders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const streamOrders = (argv: Arguments) => {
const swapsRequest = new xudrpc.SubscribeSwapsRequest();
swapsRequest.setIncludeTaker(true);
const swapsSubscription = loadXudClient(argv).subscribeSwaps(swapsRequest);
swapsSubscription.on('data', (swapResult: xudrpc.SwapResult) => {
swapsSubscription.on('data', (swapResult: xudrpc.SwapSuccess) => {
console.log(`Order swapped: ${JSON.stringify(swapResult.toObject())}`);
});

Expand Down
28 changes: 18 additions & 10 deletions lib/cli/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Arguments, Argv } from 'yargs';
import { callback, loadXudClient } from './command';
import { PlaceOrderRequest, PlaceOrderEvent, OrderSide, PlaceOrderResponse, Order, SwapResult } from '../proto/xudrpc_pb';
import { PlaceOrderRequest, PlaceOrderEvent, OrderSide, PlaceOrderResponse, Order, SwapSuccess, SwapFailure } from '../proto/xudrpc_pb';

export const orderBuilder = (argv: Argv, command: string) => argv
.option('quantity', {
Expand Down Expand Up @@ -55,19 +55,22 @@ export const orderHandler = (argv: Arguments, isSell = false) => {
console.log(JSON.stringify(response.toObject(), undefined, 2));
} else {
const internalMatch = response.getInternalMatch();
const swapResult = response.getSwapResult();
const swapSuccess = response.getSwapSuccess();
const remainingOrder = response.getRemainingOrder();
const swapFailure = response.getSwapFailure();
if (internalMatch) {
noMatches = false;
formatInternalMatch(internalMatch.toObject());
} else if (swapResult) {
} else if (swapSuccess) {
noMatches = false;
formatSwapResult(swapResult.toObject());
formatSwapSuccess(swapSuccess.toObject());
} else if (remainingOrder) {
if (noMatches) {
console.log('no matches found');
}
formatRemainingOrder(remainingOrder.toObject());
} else if (swapFailure) {
formatSwapFailure(swapFailure.toObject());
}
}
});
Expand All @@ -77,12 +80,12 @@ export const orderHandler = (argv: Arguments, isSell = false) => {
};

const formatPlaceOrderOutput = (response: PlaceOrderResponse.AsObject) => {
const { internalMatchesList, swapResultsList, remainingOrder } = response;
if (internalMatchesList.length === 0 && swapResultsList.length === 0) {
const { internalMatchesList, swapSuccessesList, remainingOrder } = response;
if (internalMatchesList.length === 0 && swapSuccessesList.length === 0) {
console.log('no matches found');
} else {
internalMatchesList.forEach(formatInternalMatch);
swapResultsList.forEach(formatSwapResult);
swapSuccessesList.forEach(formatSwapSuccess);
}
if (remainingOrder) {
formatRemainingOrder(remainingOrder);
Expand All @@ -94,9 +97,14 @@ const formatInternalMatch = (order: Order.AsObject) => {
console.log(`matched ${order.quantity} ${baseCurrency} @ ${order.price} with own order ${order.id}`);
};

const formatSwapResult = (swapResult: SwapResult.AsObject) => {
const baseCurrency = getBaseCurrency(swapResult.pairId);
console.log(`swapped ${swapResult.quantity} ${baseCurrency} with peer order ${swapResult.orderId}`);
const formatSwapSuccess = (swapSuccess: SwapSuccess.AsObject) => {
const baseCurrency = getBaseCurrency(swapSuccess.pairId);
console.log(`swapped ${swapSuccess.quantity} ${baseCurrency} with peer order ${swapSuccess.orderId}`);
};

const formatSwapFailure = (swapFailure: SwapFailure.AsObject) => {
const baseCurrency = getBaseCurrency(swapFailure.pairId);
console.log(`failed to swap ${swapFailure.quantity} ${baseCurrency} with peer order ${swapFailure.orderId}`);
};

const formatRemainingOrder = (order: Order.AsObject) => {
Expand Down
57 changes: 37 additions & 20 deletions lib/grpc/GrpcService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import Logger from '../Logger';
import Service from '../service/Service';
import * as xudrpc from '../proto/xudrpc_pb';
import { ResolveRequest, ResolveResponse } from '../proto/lndrpc_pb';
import { Order, isOwnOrder, OrderPortion } from '../types/orders';
import { Order, isOwnOrder, OrderPortion, PeerOrder } from '../types/orders';
import { errorCodes as orderErrorCodes } from '../orderbook/errors';
import { errorCodes as serviceErrorCodes } from '../service/errors';
import { errorCodes as p2pErrorCodes } from '../p2p/errors';
import { errorCodes as lndErrorCodes } from '../lndclient/errors';
import { LndInfo } from '../lndclient/LndClient';
import { PlaceOrderResult, PlaceOrderEvent, PlaceOrderEventCase } from '../types/orderBook';
import { PlaceOrderResult, PlaceOrderEvent, PlaceOrderEventType } from '../types/orderBook';
import { SwapResult } from 'lib/swaps/types';
import { fail } from 'assert';
import { request } from 'https';

/**
* Creates an xudrpc Order message from a [[StampedOrder]].
Expand All @@ -36,10 +38,10 @@ const createOrder = (order: Order) => {
};

/**
* Creates an xudrpc SwapResult message from a [[SwapResult]].
* Creates an xudrpc SwapSuccess message from a [[SwapResult]].
*/
const createSwapResult = (result: SwapResult) => {
const swapResult = new xudrpc.SwapResult();
const createSwapSuccess = (result: SwapResult) => {
const swapResult = new xudrpc.SwapSuccess();
swapResult.setOrderId(result.orderId);
swapResult.setLocalId(result.localId);
swapResult.setPairId(result.pairId);
Expand All @@ -54,6 +56,18 @@ const createSwapResult = (result: SwapResult) => {
return swapResult;
};

/**
* Creates an xudrpc SwapFailure message from a [[PeerOrder]] that could not be swapped.
*/
const createSwapFailure = (order: PeerOrder) => {
const swapFailure = new xudrpc.SwapFailure();
swapFailure.setOrderId(order.id);
swapFailure.setPairId(order.pairId);
swapFailure.setPeerPubKey(order.peerPubKey);
swapFailure.setQuantity(order.quantity);
return swapFailure;
};

/**
* Creates an xudrpc PlaceOrderResponse message from a [[PlaceOrderResult]].
*/
Expand All @@ -63,8 +77,8 @@ const createPlaceOrderResponse = (result: PlaceOrderResult) => {
const internalMatches = result.internalMatches.map(match => createOrder(match));
response.setInternalMatchesList(internalMatches);

const swapResults = result.swapResults.map(swapResult => createSwapResult(swapResult));
response.setSwapResultsList(swapResults);
const swapSuccesses = result.swapResults.map(swapResult => createSwapSuccess(swapResult));
response.setSwapSuccessesList(swapSuccesses);

if (result.remainingOrder) {
response.setRemainingOrder(createOrder(result.remainingOrder));
Expand All @@ -77,19 +91,22 @@ const createPlaceOrderResponse = (result: PlaceOrderResult) => {
* Creates an xudrpc PlaceOrderEvent message from a [[PlaceOrderEvent]].
*/
const createPlaceOrderEvent = (e: PlaceOrderEvent) => {
const response = new xudrpc.PlaceOrderEvent();
switch (e.case) {
case PlaceOrderEventCase.InternalMatch:
response.setInternalMatch(createOrder(e.payload as Order));
const placeOrderEvent = new xudrpc.PlaceOrderEvent();
switch (e.type) {
case PlaceOrderEventType.InternalMatch:
placeOrderEvent.setInternalMatch(createOrder(e.payload as Order));
break;
case PlaceOrderEventType.SwapSuccess:
placeOrderEvent.setSwapSuccess(createSwapSuccess(e.payload as SwapResult));
break;
case PlaceOrderEventCase.SwapResult:
response.setSwapResult(createSwapResult(e.payload as SwapResult));
case PlaceOrderEventType.RemainingOrder:
placeOrderEvent.setRemainingOrder(createOrder(e.payload as Order));
break;
case PlaceOrderEventCase.RemainingOrder:
response.setRemainingOrder(createOrder(e.payload as Order));
case PlaceOrderEventType.SwapFailure:
placeOrderEvent.setSwapFailure(createSwapFailure(e.payload as PeerOrder));
break;
}
return response;
return placeOrderEvent;
};

/** Class containing the available RPC methods for XUD */
Expand Down Expand Up @@ -276,10 +293,10 @@ class GrpcService {
/**
* See [[Service.executeSwap]]
*/
public executeSwap: grpc.handleUnaryCall<xudrpc.ExecuteSwapRequest, xudrpc.SwapResult> = async (call, callback) => {
public executeSwap: grpc.handleUnaryCall<xudrpc.ExecuteSwapRequest, xudrpc.SwapSuccess> = async (call, callback) => {
try {
const result = await this.service.executeSwap(call.request.toObject());
callback(null, createSwapResult(result));
callback(null, createSwapSuccess(result));
} catch (err) {
callback(this.getGrpcError(err), null);
}
Expand Down Expand Up @@ -551,9 +568,9 @@ class GrpcService {
/*
* See [[Service.subscribeSwaps]]
*/
public subscribeSwaps: grpc.handleServerStreamingCall<xudrpc.SubscribeSwapsRequest, xudrpc.SwapResult> = (call) => {
public subscribeSwaps: grpc.handleServerStreamingCall<xudrpc.SubscribeSwapsRequest, xudrpc.SwapSuccess> = (call) => {
this.service.subscribeSwaps(call.request.toObject(), (result: SwapResult) => {
call.write(createSwapResult(result));
call.write(createSwapSuccess(result));
});
this.addStream(call);
}
Expand Down
11 changes: 6 additions & 5 deletions lib/orderbook/OrderBook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Swaps from '../swaps/Swaps';
import { SwapRole, SwapFailureReason, SwapPhase } from '../types/enums';
import { CurrencyInstance, PairInstance, CurrencyFactory } from '../types/db';
import { Pair, OrderIdentifier, OwnOrder, OrderPortion, OwnLimitOrder, PeerOrder, Order } from '../types/orders';
import { PlaceOrderEvent, PlaceOrderEventCase, PlaceOrderResult } from '../types/orderBook';
import { PlaceOrderEvent, PlaceOrderEventType, PlaceOrderResult } from '../types/orderBook';
import { SwapRequestPacket, SwapFailedPacket } from '../p2p/packets';
import { SwapResult, SwapDeal } from 'lib/swaps/types';
import Bluebird from 'bluebird';
Expand Down Expand Up @@ -243,7 +243,7 @@ class OrderBook extends EventEmitter {
const stampedOrder = this.stampOwnOrder(order);
if (this.nomatching) {
this.addOwnOrder(stampedOrder);
onUpdate && onUpdate({ case: PlaceOrderEventCase.RemainingOrder, payload: stampedOrder });
onUpdate && onUpdate({ type: PlaceOrderEventType.RemainingOrder, payload: stampedOrder });

return {
internalMatches: [],
Expand Down Expand Up @@ -310,7 +310,7 @@ class OrderBook extends EventEmitter {
result.internalMatches.push(maker);
this.emit('ownOrder.filled', portion);
await this.persistTrade(portion.quantity, maker, taker);
onUpdate && onUpdate({ case: PlaceOrderEventCase.InternalMatch, payload: maker });
onUpdate && onUpdate({ type: PlaceOrderEventType.InternalMatch, payload: maker });
} else {
if (!this.swaps) {
// swaps should only be undefined during integration testing of the order book
Expand All @@ -335,9 +335,10 @@ class OrderBook extends EventEmitter {
this.logger.info(`match executed on taker ${taker.id} and maker ${maker.id} for ${maker.quantity} with peer ${maker.peerPubKey}`);
}
result.swapResults.push(swapResult);
onUpdate && onUpdate({ case: PlaceOrderEventCase.SwapResult, payload: swapResult });
onUpdate && onUpdate({ type: PlaceOrderEventType.SwapSuccess, payload: swapResult });
} catch (err) {
failedSwapQuantity += portion.quantity;
onUpdate && onUpdate({ type: PlaceOrderEventType.SwapFailure, payload: maker });
this.logger.warn(`swap for ${portion.quantity} failed during order matching, will repeat matching routine for failed swap quantity`);
}
}
Expand All @@ -360,7 +361,7 @@ class OrderBook extends EventEmitter {
const { remainingOrder } = result;
if (remainingOrder && !discardRemaining) {
this.addOwnOrder(remainingOrder);
onUpdate && onUpdate({ case: PlaceOrderEventCase.RemainingOrder, payload: remainingOrder });
onUpdate && onUpdate({ type: PlaceOrderEventType.RemainingOrder, payload: remainingOrder });
}

return result;
Expand Down
Loading

0 comments on commit 6c9c2a6

Please sign in to comment.