Skip to content

Commit f427158

Browse files
committed
feat(swapclient): heartbeat
This commit shortens the interval for the outbound capacity check timer from 60 to 3 seconds and sets the client as disconnected any time a call fails due to an unreachable server. This makes the capacity checks act like a heartbeat, checking that the server is reachable every few seconds even in the absence of any other activity. Previously, we had used a dummy server -> client streaming call and listened for the `error` event on the lnd side, however with newer versions of lnd and grpc this is no longer a reliable way to tell when lnd has gone down. This also resolves an issue where lnd would get stuck in the `WaitingUnlock` state if it is stopped while xud is running and comes back online in the locked state. Closes #1090.
1 parent e1ee78a commit f427158

File tree

3 files changed

+16
-31
lines changed

3 files changed

+16
-31
lines changed

lib/lndclient/LndClient.ts

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ class LndClient extends SwapClient {
4444
private urisList?: string[];
4545
/** The identifier for the chain this lnd instance is using in the format [chain]-[network] like "bitcoin-testnet" */
4646
private chainIdentifier?: string;
47-
private channelSubscription?: ClientReadableStream<lndrpc.ChannelEventUpdate>;
4847
private invoiceSubscriptions = new Map<string, ClientReadableStream<lndrpc.Invoice>>();
4948
private maximumOutboundAmount = 0;
5049
private initWalletResolve?: (value: boolean) => void;
@@ -163,6 +162,10 @@ class LndClient extends SwapClient {
163162
}
164163
(this.lightning![methodName] as Function)(params, this.meta, (err: grpc.ServiceError, response: U) => {
165164
if (err) {
165+
if (err.code === grpc.status.UNAVAILABLE) {
166+
this.disconnect().catch(this.logger.error);
167+
}
168+
this.logger.trace(`error on ${methodName}: ${err.message}`);
166169
reject(err);
167170
} else {
168171
resolve(response);
@@ -187,6 +190,10 @@ class LndClient extends SwapClient {
187190
}
188191
(this.invoices![methodName] as Function)(params, this.meta, (err: grpc.ServiceError, response: U) => {
189192
if (err) {
193+
if (err.code === grpc.status.UNAVAILABLE) {
194+
this.disconnect().catch(this.logger.error);
195+
}
196+
this.logger.trace(`error on ${methodName}: ${err.message}`);
190197
reject(err);
191198
} else {
192199
resolve(response);
@@ -203,6 +210,7 @@ class LndClient extends SwapClient {
203210
}
204211
(this.walletUnlocker![methodName] as Function)(params, this.meta, (err: grpc.ServiceError, response: U) => {
205212
if (err) {
213+
this.logger.trace(`error on ${methodName}: ${err.message}`);
206214
reject(err);
207215
} else {
208216
resolve(response);
@@ -236,7 +244,7 @@ class LndClient extends SwapClient {
236244
version = lnd.getVersion();
237245
alias = lnd.getAlias();
238246
} catch (err) {
239-
this.logger.error(`LND error: ${err}`);
247+
this.logger.error('getinfo error', err);
240248
error = err.message;
241249
}
242250
}
@@ -375,8 +383,6 @@ class LndClient extends SwapClient {
375383
this.walletUnlocker.close();
376384
this.walletUnlocker = undefined;
377385
}
378-
379-
this.subscribeChannels();
380386
} else {
381387
await this.setStatus(ClientStatus.OutOfSync);
382388
this.logger.warn(`lnd is out of sync with chain, retrying in ${LndClient.RECONNECT_TIMER} ms`);
@@ -800,25 +806,6 @@ class LndClient extends SwapClient {
800806
this.invoiceSubscriptions.set(rHash, invoiceSubscription);
801807
}
802808

803-
/**
804-
* Subscribes to channel events.
805-
*/
806-
private subscribeChannels = (): void => {
807-
if (!this.lightning) {
808-
throw errors.LND_IS_UNAVAILABLE(this.status);
809-
}
810-
if (this.channelSubscription) {
811-
this.channelSubscription.cancel();
812-
}
813-
814-
this.channelSubscription = this.lightning.subscribeChannelEvents(new lndrpc.ChannelEventSubscription(), this.meta)
815-
.on('error', async (error) => {
816-
this.channelSubscription = undefined;
817-
this.logger.error(`lnd has been disconnected, error: ${error}`);
818-
await this.disconnect();
819-
});
820-
}
821-
822809
/**
823810
* Attempts to close an open channel.
824811
*/
@@ -850,10 +837,6 @@ class LndClient extends SwapClient {
850837

851838
/** Lnd specific procedure to disconnect from the server. */
852839
protected disconnect = async () => {
853-
if (this.channelSubscription) {
854-
this.channelSubscription.cancel();
855-
this.channelSubscription = undefined;
856-
}
857840
if (this.lightning) {
858841
this.lightning.close();
859842
this.lightning = undefined;

lib/raidenclient/RaidenClient.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ class RaidenClient extends SwapClient {
129129
this.maximumOutboundAmounts.set(currency, balance);
130130
});
131131
} catch (e) {
132-
// TODO: Mark client as disconnected
133132
this.logger.error(`failed to fetch channelbalances: ${e}`);
134133
}
135134
}
@@ -391,7 +390,10 @@ class RaidenClient extends SwapClient {
391390
}
392391
});
393392

394-
req.on('error', (err) => {
393+
req.on('error', (err: any) => {
394+
if (err.code === 'ECONNREFUSED') {
395+
this.disconnect().catch(this.logger.error);
396+
}
395397
this.logger.error(err);
396398
reject(err);
397399
});

lib/swaps/SwapClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ abstract class SwapClient extends EventEmitter {
6565
private updateCapacityTimer?: NodeJS.Timer;
6666
/** The maximum amount of time we will wait for the connection to be verified during initialization. */
6767
private static INITIALIZATION_TIME_LIMIT = 5000;
68-
/** Time in milliseconds between updating the maximum outbound capacity */
69-
private static CAPACITY_REFRESH_INTERVAL = 60000;
68+
/** Time in milliseconds between updating the maximum outbound capacity. */
69+
private static CAPACITY_REFRESH_INTERVAL = 3000;
7070

7171
constructor(public logger: Logger) {
7272
super();

0 commit comments

Comments
 (0)