Skip to content

Commit

Permalink
feat: WebSocket fallback URL
Browse files Browse the repository at this point in the history
On regtest, when the backend is run without nginx and the WebSocket
is on a different port than the rest of the API, we need to use a
different endpoint for connecting to the WebSocket
  • Loading branch information
michael1011 committed Aug 15, 2024
1 parent 255e511 commit 8659a57
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 38 deletions.
7 changes: 5 additions & 2 deletions src/components/CreateButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,11 @@ export const CreateButton = () => {

const buttonClick = async () => {
setButtonDisable(true);
await create();
setButtonDisable(false);
try {
await create();
} finally {
setButtonDisable(false);
}
};

const getButtonLabel = (label: ButtonLabelParams) => {
Expand Down
85 changes: 52 additions & 33 deletions src/components/SwapChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { OutputType } from "boltz-core";
import log from "loglevel";
import { createEffect, onCleanup, onMount } from "solid-js";

import { config } from "../config";
import { RBTC } from "../consts/Assets";
import { SwapType } from "../consts/Enums";
import {
Expand Down Expand Up @@ -41,45 +42,20 @@ class BoltzWebSocket {

constructor(
private readonly url: string,
private readonly wsFallback: string | undefined,
private readonly relevantIds: Set<string>,
private readonly prepareSwap: (id: string, status: any) => void,
private readonly claimSwap: (id: string, status: any) => Promise<void>,
) {}

public connect = () => {
this.isClosed = false;
clearTimeout(this.reconnectTimeout);
this.ws?.close();
this.ws = new WebSocket(
`${BoltzWebSocket.formatWsUrl(this.url)}/v2/ws`,
);

this.ws.onopen = () => {
this.subscribeUpdates(Array.from(this.relevantIds.values()));
};
this.ws.onclose = () => {
log.warn(`ws ${this.url} closed`);
this.handleClose();
};
this.ws.onmessage = async (msg) => {
const data = JSON.parse(msg.data);
if (data.event === "pong" || data.event === "ping") {
return;
log.debug("Opening WebSocket");
this.openWebSocket(`${this.url}/v2/ws`).catch(() => {
if (this.wsFallback !== undefined) {
log.debug("Opening fallback WebSocket");
this.openWebSocket(this.wsFallback).then().catch();
}

log.debug(`ws ${this.url} message`, data);

if (data.event === "update" && data.channel === "swap.update") {
const swapUpdates = data.args as SwapStatus[];
for (const status of swapUpdates) {
this.relevantIds.add(status.id);
this.prepareSwap(status.id, status);
await this.swapClaimLock.acquire(() =>
this.claimSwap(status.id, status),
);
}
}
};
});
};

public close = () => {
Expand All @@ -106,6 +82,49 @@ class BoltzWebSocket {
);
};

private openWebSocket = async (url: string) => {
this.isClosed = false;
clearTimeout(this.reconnectTimeout);
this.ws?.close();

return new Promise<void>((resolve, reject) => {
this.ws = new WebSocket(BoltzWebSocket.formatWsUrl(url));

this.ws.onopen = () => {
this.subscribeUpdates(Array.from(this.relevantIds.values()));
};
this.ws.onclose = (error) => {
log.warn("WebSocket closed", error);
this.handleClose();

if (error.wasClean) {
resolve();
} else {
reject(error);
}
};
this.ws.onmessage = async (msg) => {
const data = JSON.parse(msg.data);
if (data.event === "pong" || data.event === "ping") {
return;
}

log.debug("WebSocket message", data);

if (data.event === "update" && data.channel === "swap.update") {
const swapUpdates = data.args as SwapStatus[];
for (const status of swapUpdates) {
this.relevantIds.add(status.id);
this.prepareSwap(status.id, status);
await this.swapClaimLock.acquire(() =>
this.claimSwap(status.id, status),
);
}
}
};
});
};

private handleClose = () => {
// Don't reconnect when it has been closed manually
if (this.isClosed) {
Expand Down Expand Up @@ -249,9 +268,9 @@ export const SwapChecker = () => {
s.claimTx === undefined),
);

log.debug(`Opening WebSocket: ${getApiUrl()}`);
ws = new BoltzWebSocket(
getApiUrl(),
config.apiUrl.wsFallback,
new Set<string>(swapsToCheck.map((s) => s.id)),
prepareSwap,
claimSwap,
Expand Down
5 changes: 3 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const defaults = {
};

type Asset = {
apiUrl?: Url;
network?: any;
blockExplorerUrl?: Url;

Expand All @@ -45,7 +44,9 @@ type Url = {
};

export type Config = {
apiUrl?: Url;
// The wsFallback is used on regtest when the backend is being run without
// nginx and the WebSocket is on a different port than the rest of the API
apiUrl?: Url & { wsFallback?: string };
network?: "mainnet" | "testnet" | "regtest";
isBoltzClient?: boolean;
boltzClientApiUrl?: string;
Expand Down
3 changes: 2 additions & 1 deletion src/configs/regtest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"network": "regtest",
"loglevel": "debug",
"apiUrl": {
"normal": "http://localhost:9001"
"normal": "http://localhost:9001",
"wsFallback": "http://localhost:9004"
},
"assets": {
"BTC": {
Expand Down

0 comments on commit 8659a57

Please sign in to comment.