Skip to content

Commit

Permalink
feat: add button to auto-balance LN channels
Browse files Browse the repository at this point in the history
Solves jamaljsr#831.
  • Loading branch information
uwla committed Mar 23, 2024
1 parent 9ae4cd9 commit 01e3f8c
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/components/designer/AutoBalanceButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import styled from '@emotion/styled';
import { Button } from 'antd';
import { useStoreActions } from 'store';
import { Network } from 'types';

const Styled = {
Button: styled(Button)`
margin-left: 8px;
`,
};

interface Props {
network: Network;
}

const AutoBalanceButton: React.FC<Props> = ({ network }) => {
const { autoBalanceChannels } = useStoreActions(s => s.network);
const { notify } = useStoreActions(s => s.app);

const handleClick = async () => {
try {
autoBalanceChannels({ id: network.id });
} catch (error: any) {
notify({ message: 'Failed to auto-balance channels', error });
}
};

return <Styled.Button onClick={handleClick}>Auto Balance channels</Styled.Button>;
};

export default AutoBalanceButton;
2 changes: 2 additions & 0 deletions src/components/network/NetworkActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Status } from 'shared/types';
import { useStoreState } from 'store';
import { Network } from 'types';
import { getNetworkBackendId } from 'utils/network';
import AutoBalanceButton from 'components/designer/AutoBalanceButton';

const Styled = {
Button: styled(Button)`
Expand Down Expand Up @@ -130,6 +131,7 @@ const NetworkActions: React.FC<Props> = ({
</Button>
<AutoMineButton network={network} />
<SyncButton network={network} />
<AutoBalanceButton network={network} />
<Divider type="vertical" />
</>
)}
Expand Down
1 change: 1 addition & 0 deletions src/store/models/lightning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const lightningModel: LightningModel = {
const api = injections.lightningFactory.getService(node);
const channels = await api.getChannels(node);
actions.setChannels({ node, channels });
return channels;
}),
getAllInfo: thunk(async (actions, node) => {
await actions.getInfo(node);
Expand Down
50 changes: 50 additions & 0 deletions src/store/models/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import {
TapdNode,
TapNode,
} from 'shared/types';
import { LightningNodeChannel } from 'lib/lightning/types';
import { AutoMineMode, CustomImage, Network, StoreInjections } from 'types';
import { delay } from 'utils/async';
import { initChartFromNetwork } from 'utils/chart';
import { APP_VERSION, DOCKER_REPO } from 'utils/constants';
import { rm } from 'utils/files';
import {
balanceChannel,
createBitcoindNetworkNode,
createCLightningNetworkNode,
createEclairNetworkNode,
Expand Down Expand Up @@ -178,6 +180,9 @@ export interface NetworkModel {
setAutoMineMode: Action<NetworkModel, { id: number; mode: AutoMineMode }>;
setMiningState: Action<NetworkModel, { id: number; mining: boolean }>;
mineBlock: Thunk<NetworkModel, { id: number }, StoreInjections, RootModel>;

/* */
autoBalanceChannels: Thunk<NetworkModel, { id: number }, StoreInjections, RootModel>;
}

const networkModel: NetworkModel = {
Expand Down Expand Up @@ -922,6 +927,51 @@ const networkModel: NetworkModel = {

actions.setAutoMineMode({ id, mode });
}),
autoBalanceChannels: thunk(
async (actions, { id }, { getState, getStoreState, getStoreActions }) => {
const { networks } = getState();
const network = networks.find(n => n.id === id);
if (!network) throw new Error(l('networkByIdErr', { id }));

const { createInvoice, payInvoice, getChannels } = getStoreActions().lightning;

// Store all channels in an array and build a map nodeName->node.
const lnNodes = network.nodes.lightning;
const channels = [] as LightningNodeChannel[];
const id2Node = {} as Record<string, LightningNode>;
for (const node of lnNodes) {
const nodeChannels = await getChannels(node);
channels.push(...nodeChannels);
id2Node[node.name] = node;
}

const minimumSatsDifference = 150; // TODO: put it somewhere else.
const links = getStoreState().designer.activeChart.links;
const promisesToAwait = [] as Promise<unknown>[];
for (const channel of channels) {
const id = channel.uniqueId;
const { to, from } = links[id];
const fromNode = id2Node[from.nodeId as string];
const toNode = id2Node[to.nodeId as string];
const info = balanceChannel(channel, fromNode, toNode);
const { source, target, amount } = info;

console.log(`[AUTO BALANCE] ${source.name} -> ${amount} -> ${target.name}`);

// Skip balancing if amount is too small.
if (amount < minimumSatsDifference) continue;

// Let's avoid problems with promises inside loops.
promisesToAwait.push(
createInvoice({ node: source, amount }).then(invoice =>
payInvoice({ node: target, amount, invoice }),
),
);
}

await Promise.all(promisesToAwait);
},
),
};

export default networkModel;
14 changes: 14 additions & 0 deletions src/utils/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
TapNode,
} from 'shared/types';
import { createIpcSender } from 'lib/ipc/ipcService';
import { LightningNodeChannel } from 'lib/lightning/types';
import {
AutoMineMode,
CustomImage,
Expand Down Expand Up @@ -58,6 +59,19 @@ const groupNodes = (network: Network) => {
};
};

export const balanceChannel = (
channel: LightningNodeChannel,
localNode: LightningNode,
remoteNode: LightningNode,
) => {
const localBalance = Number(channel.localBalance);
const remoteBalance = Number(channel.remoteBalance);
const amount = Math.floor(Math.abs(localBalance - remoteBalance) / 2);
const source = localBalance > remoteBalance ? localNode : remoteNode;
const target = localBalance > remoteBalance ? remoteNode : localNode;
return { source, target, amount };
};

export const getImageCommand = (
images: ManagedImage[],
implementation: NodeImplementation,
Expand Down

0 comments on commit 01e3f8c

Please sign in to comment.