Skip to content

Commit

Permalink
fix(swapclients): check capacity up front
Browse files Browse the repository at this point in the history
This fixes a bug where the capacity for swap clients is thought to be 0
upon initialization. The checks to update the capacity happen on an
interval but not right away. This adds logic to check the capacity
immediately.

Fixes #900.
  • Loading branch information
sangaman committed Apr 18, 2019
1 parent 082d059 commit 1fed617
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 41 deletions.
31 changes: 18 additions & 13 deletions lib/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ abstract class BaseClient extends EventEmitter {
public maximumOutboundCapacity = 0;
protected status: ClientStatus = ClientStatus.NotInitialized;
protected reconnectionTimer?: NodeJS.Timer;

/** Time in milliseconds between attempts to recheck connectivity to the client. */
protected static readonly RECONNECT_TIMER = 5000;

private updateCapacityTimer?: NodeJS.Timer;
/** Time in milliseconds between updating the maximum outbound capacity */
private CAPACITY_REFRESH_INTERVAL = 60000;
private static CAPACITY_REFRESH_INTERVAL = 60000;

constructor(protected logger: Logger) {
super();
Expand All @@ -45,28 +45,33 @@ abstract class BaseClient extends EventEmitter {
* Returns the total balance available across all channels.
*/
public abstract channelBalance(): Promise<ChannelBalance>;
protected setStatus(status: ClientStatus): void {

protected setStatus = async (status: ClientStatus): Promise<void> => {
this.logger.info(`${this.constructor.name} status: ${ClientStatus[status]}`);
this.status = status;
this.checkTimers();
await this.setTimers();
}
private checkTimers() {

private setTimers = async () => {
if (this.status === ClientStatus.ConnectionVerified) {
this.updateCapacityTimer = setInterval(async () => {
try {
this.maximumOutboundCapacity = (await this.channelBalance()).balance;
} catch (e) {
// TODO: Mark client as disconnected
this.logger.error(`failed to fetch channelbalance from client: ${e}`);
}
}, this.CAPACITY_REFRESH_INTERVAL);
await this.updateCapacity();
this.updateCapacityTimer = setInterval(this.updateCapacity, BaseClient.CAPACITY_REFRESH_INTERVAL);
} else {
if (this.updateCapacityTimer) {
clearInterval(this.updateCapacityTimer);
}
}
}

private updateCapacity = async () => {
try {
this.maximumOutboundCapacity = (await this.channelBalance()).balance;
} catch (e) {
// TODO: Mark client as disconnected
this.logger.error(`failed to fetch channelbalance from client: ${e}`);
}
}

/**
* Sends payment according to the terms of a swap deal.
* @returns the preimage for the swap
Expand Down
16 changes: 8 additions & 8 deletions lib/lndclient/LndClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class LndClient extends BaseClient {
shouldDisable = true;
}
if (shouldDisable) {
this.setStatus(ClientStatus.Disabled);
await this.setStatus(ClientStatus.Disabled);
return;
}

Expand All @@ -96,8 +96,8 @@ class LndClient extends BaseClient {
this.logger.info(`macaroons are disabled for lnd for ${this.currency}`);
}
// set status as disconnected until we can verify the connection
this.setStatus(ClientStatus.Disconnected);
return this.verifyConnection();
await this.setStatus(ClientStatus.Disconnected);
await this.verifyConnection();
}

public get pubKey() {
Expand Down Expand Up @@ -178,7 +178,7 @@ class LndClient extends BaseClient {
const getInfoResponse = await this.getInfo();
if (getInfoResponse.getSyncedToChain()) {
// mark connection as active
this.setStatus(ClientStatus.ConnectionVerified);
await this.setStatus(ClientStatus.ConnectionVerified);
if (this.reconnectionTimer) {
clearTimeout(this.reconnectionTimer);
this.reconnectionTimer = undefined;
Expand All @@ -193,12 +193,12 @@ class LndClient extends BaseClient {
this.emit('connectionVerified', newPubKey);
this.subscribeInvoices();
} else {
this.setStatus(ClientStatus.OutOfSync);
await this.setStatus(ClientStatus.OutOfSync);
this.logger.error(`lnd for ${this.currency} is out of sync with chain, retrying in ${LndClient.RECONNECT_TIMER} ms`);
this.reconnectionTimer = setTimeout(this.verifyConnection, LndClient.RECHECK_SYNC_TIMER);
}
} catch (err) {
this.setStatus(ClientStatus.Disconnected);
await this.setStatus(ClientStatus.Disconnected);
this.logger.error(`could not verify connection to lnd for ${this.currency} at ${this.uri}, error: ${JSON.stringify(err)},
retrying in ${LndClient.RECONNECT_TIMER} ms`);
this.reconnectionTimer = setTimeout(this.verifyConnection, LndClient.RECONNECT_TIMER);
Expand Down Expand Up @@ -382,9 +382,9 @@ class LndClient extends BaseClient {
}

this.invoiceSubscription = this.lightning.subscribeInvoices(new lndrpc.InvoiceSubscription(), this.meta)
.on('error', (error) => {
.on('error', async (error) => {
this.invoiceSubscription = undefined;
this.setStatus(ClientStatus.Disconnected);
await this.setStatus(ClientStatus.Disconnected);
this.logger.error(`lnd for ${this.currency} has been disconnected, error: ${error}`);
this.reconnectionTimer = setTimeout(this.verifyConnection, LndClient.RECONNECT_TIMER);
});
Expand Down
2 changes: 1 addition & 1 deletion lib/orderbook/OrderBook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ class OrderBook extends EventEmitter {
throw errors.SWAP_CLIENT_NOT_FOUND(makerCurrency);
}
if (makerAmount > swapClient.maximumOutboundCapacity) {
throw errors.INSUFFICIENT_OUTBOUND_BALANCE(makerCurrency, makerAmount);
throw errors.INSUFFICIENT_OUTBOUND_BALANCE(makerCurrency, makerAmount, swapClient.maximumOutboundCapacity);
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/orderbook/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ const errors = {
message: `unable to find swap client for currency ${currency}`,
code: errorCodes.SWAP_CLIENT_NOT_FOUND,
}),
INSUFFICIENT_OUTBOUND_BALANCE: (currency: string, amount: number) => ({
message: `${currency} does not have sufficient outbound balance of: ${amount}`,
INSUFFICIENT_OUTBOUND_BALANCE: (currency: string, amount: number, availableAmount: number) => ({
message: `${currency} outbound balance of ${availableAmount} is not sufficient for order amount of ${amount}`,
code: errorCodes.INSUFFICIENT_OUTBOUND_BALANCE,
}),
};
Expand Down
15 changes: 6 additions & 9 deletions lib/raidenclient/RaidenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class RaidenClient extends BaseClient {
public tokenAddresses = new Map<string, string>();
private port: number;
private host: string;
private disable: boolean;
private repository: OrderBookRepository;

/**
Expand All @@ -96,26 +97,21 @@ class RaidenClient extends BaseClient {

this.port = port;
this.host = host;
this.disable = disable;

this.repository = new OrderBookRepository(logger, models);

if (disable) {
this.setStatus(ClientStatus.Disabled);
}
}

/**
* Checks for connectivity and gets our Raiden account address
*/
public init = async () => {
if (this.isDisabled()) {
this.logger.error(`can't init raiden. raiden is disabled`);
if (this.disable) {
await this.setStatus(ClientStatus.Disabled);
return;
}
// associate the client with all currencies that have a contract address
await this.setCurrencies();
// set status as disconnected until we can verify the connection
this.setStatus(ClientStatus.Disconnected);
await this.verifyConnection();
}

Expand Down Expand Up @@ -152,7 +148,7 @@ class RaidenClient extends BaseClient {
}

this.emit('connectionVerified', newAddress);
this.setStatus(ClientStatus.ConnectionVerified);
await this.setStatus(ClientStatus.ConnectionVerified);
} catch (err) {
this.logger.error(
`could not verify connection to raiden at ${this.host}:${this.port}, retrying in ${RaidenClient.RECONNECT_TIMER} ms`,
Expand Down Expand Up @@ -321,6 +317,7 @@ class RaidenClient extends BaseClient {
* Returns the total balance available across all channels.
*/
public channelBalance = async (): Promise<ChannelBalance> => {
// TODO: refine logic to determine balance per token rather than all combined
const channels = await this.getChannels();
const balance = channels.filter(channel => channel.state === 'opened')
.map(channel => channel.balance)
Expand Down
2 changes: 1 addition & 1 deletion test/simulation/xud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func testOrderMatchingAndSwap(net *xudtest.NetworkHarness, ht *harnessTest) {
func testOrderBroadcastAndInvalidation(net *xudtest.NetworkHarness, ht *harnessTest) {
req := &xudrpc.PlaceOrderRequest{
Price: 10,
Quantity: 1000000000,
Quantity: 1000000,
PairId: "LTC/BTC",
OrderId: "random_order_id",
Side: xudrpc.OrderSide_BUY,
Expand Down
14 changes: 7 additions & 7 deletions test/simulation/xudtest/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ package xudtest
import (
"bytes"
"fmt"
"github.com/ExchangeUnion/xud-simulation/lntest"
"github.com/ExchangeUnion/xud-simulation/xudrpc"
"github.com/go-errors/errors"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"net"
"os"
"os/exec"
Expand All @@ -17,6 +11,13 @@ import (
"sync"
"sync/atomic"
"time"

"github.com/ExchangeUnion/xud-simulation/lntest"
"github.com/ExchangeUnion/xud-simulation/xudrpc"
"github.com/go-errors/errors"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)

var (
Expand Down Expand Up @@ -50,7 +51,6 @@ func (cfg nodeConfig) genArgs() []string {
var args []string

args = append(args, "--initdb=false")
args = append(args, "--nosanitychecks=true")
args = append(args, "--loglevel=debug")
args = append(args, "--raiden.disable")

Expand Down

0 comments on commit 1fed617

Please sign in to comment.