Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ on:
description: Image to build
required: true
type: choice
options: [bitcoind, lnd, clightning]
options: [bitcoind, lnd, clightning, lampo]
tag:
description: Image tag (ex. 29.4.2, 0.21.3-beta-blake2b.17, 26.06.8-blake2b.6)
description: Image tag (ex. 29.4.2, 0.21.3-beta-blake2b.17, 26.06.8-blake2b.6, 0.1.0-blake2b.1)
required: true
build_args:
description: Build args, one KEY=VALUE per line (see docker/README.md)
Expand Down
15 changes: 15 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ $ docker buildx build --platform linux/amd64,linux/arm64 --build-arg CLN_VERSION

Replace `<version>` with a release of [privkeyio/lightning](https://github.com/privkeyio/lightning) without the leading `v` (ex: `26.06.8-blake2b.6`). The image builds on `ghcr.io/privkeyio/lightningd`, which privkeyio/lightning publishes for each release.

## Lampo

### Tags

- `0.1.0-blake2b.1` ([lampo/Dockerfile](lampo/Dockerfile))

**Building the image**

```sh
$ cd lampo
$ docker buildx build --platform linux/amd64,linux/arm64 --build-arg LAMPO_VERSION=<version> --build-arg LAMPO_COMMIT=<commit> -t ghcr.io/privkeyio/polar/lampo:<version> --push .
```

Replace `<version>` with a tag of [privkeyio/lampo.rs](https://github.com/privkeyio/lampo.rs) without the leading `v` (ex: `0.1.0-blake2b.1`) and `<commit>` with the commit that tag points at. It is built from source, and the build fails if the tag has moved.

The images can also be built and pushed from master with the `Images` workflow in GitHub Actions, passing the same build args one per line.

## SimLN
Expand Down
35 changes: 35 additions & 0 deletions docker/lampo/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM rust:1-bookworm AS builder

ARG LAMPO_VERSION
ARG LAMPO_COMMIT

RUN git clone --depth 1 --branch v${LAMPO_VERSION} https://github.com/privkeyio/lampo.rs /src \
&& cd /src \
&& test "$(git rev-parse HEAD)" = "${LAMPO_COMMIT}" \
&& cargo build --release --locked -p lampod-cli -p lampo-cli \
&& cargo build --release --locked -p lampo-plugin-sdk --example lampo-bitcoind \
&& mkdir /out \
&& cp target/release/lampod-cli target/release/lampo-cli target/release/examples/lampo-bitcoind /out/

FROM debian:bookworm-slim

ENV PATH=/opt/lampo:$PATH

RUN apt-get update -y \
&& apt-get install -y ca-certificates curl gosu bash \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

COPY --from=builder /out/ /opt/lampo/

COPY docker-entrypoint.sh /entrypoint.sh

RUN chmod a+x /entrypoint.sh

VOLUME ["/home/lampo/.lampo"]

EXPOSE 9735 7979

ENTRYPOINT ["/entrypoint.sh"]

CMD ["lampod-cli"]
58 changes: 58 additions & 0 deletions docker/lampo/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/sh
set -e

# containers on linux share file permissions with hosts.
# assigning the same uid/gid from the host user
# ensures that the files can be read/write from both sides
if ! id lampo > /dev/null 2>&1; then
USERID=${USERID:-1000}
GROUPID=${GROUPID:-1000}

echo "adding user lampo ($USERID:$GROUPID)"
groupadd -f -g $GROUPID lampo
useradd -r -u $USERID -g $GROUPID lampo
chown -R $USERID:$GROUPID /home/lampo
fi

if [ $(echo "$1" | cut -c1) = "-" ]; then
echo "$0: assuming arguments for lampod-cli"

set -- lampod-cli "$@"
fi

LAMPO_DIR=/home/lampo/.lampo/regtest
mkdir -p $LAMPO_DIR
cat > $LAMPO_DIR/lampo.conf <<CONF
network=regtest
port=9735
bind-addr=0.0.0.0
alias=$(hostname)
backend=core
dev-sync=true
CONF
chown -R "$(id -u lampo):$(id -g lampo)" /home/lampo/.lampo

if [ "$1" = "lampod-cli" ] && [ ! -f $LAMPO_DIR/wallet.dat ]; then
# an interrupted wallet creation leaves a database without its seed,
# which a new wallet cannot be created over
rm -f $LAMPO_DIR/bdk-wallet.db
for arg in "$@"; do
case "$arg" in
--core-url=*) CORE_URL="${arg#--core-url=}" ;;
esac
done
if [ -n "$CORE_URL" ]; then
echo "Waiting for $CORE_URL"
until curl -s -o /dev/null "$CORE_URL"; do sleep 1; done
fi
echo "Creating the lampo wallet"
gosu lampo "$@" new-wallet
fi

if [ "$1" = "lampod-cli" ] || [ "$1" = "lampo-cli" ]; then
echo "Running as lampo user: $@"
exec gosu lampo "$@"
fi

echo "$@"
exec "$@"
6 changes: 5 additions & 1 deletion docker/nodes.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 1,
"version": 2,
"images": {
"LND": {
"latest": "0.21.3-beta-blake2b.17",
Expand All @@ -12,6 +12,10 @@
"latest": "26.06.8-blake2b.6",
"versions": ["26.06.8-blake2b.6"]
},
"lampo": {
"latest": "0.1.0-blake2b.1",
"versions": ["0.1.0-blake2b.1"]
},
"bitcoind": {
"latest": "29.4.2",
"versions": ["29.4.2"]
Expand Down
1 change: 1 addition & 0 deletions src/components/common/ImageUpdatesModal.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ describe('ImageUpdatesModal', () => {
updates: {
LND: ['1.2.3'],
'c-lightning': [],
lampo: [],
bitcoind: ['4.5.6'],
btcd: [],
},
Expand Down
1 change: 1 addition & 0 deletions src/components/common/RenameNodeModal.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('RenameNodeModal', () => {
description: 'network description',
lndNodes: 2,
clightningNodes: 1,
lampoNodes: 0,
bitcoindNodes: 3,
status,
repoState: defaultRepoState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('SendOnChainModal', () => {
description: 'network description',
lndNodes: 2,
clightningNodes: 1,
lampoNodes: 0,
bitcoindNodes: 3,
status: Status.Started,
repoState: defaultRepoState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('AddSimulationModal', () => {
description: 'network description',
lndNodes: 2,
clightningNodes: 0,
lampoNodes: 0,
bitcoindNodes: 2,
repoState: defaultRepoState,
managedImages: testManagedImages,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('SimulationDesignerTab', () => {
description: 'network description',
lndNodes: 2,
clightningNodes: 0,
lampoNodes: 0,
bitcoindNodes: 2,
repoState: defaultRepoState,
managedImages: testManagedImages,
Expand Down
11 changes: 10 additions & 1 deletion src/components/designer/lightning/ConnectTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import styled from '@emotion/styled';
import * as LND from '@lightningpolar/lnd-api';
import { Alert, Button, Radio, Tooltip } from 'antd';
import { usePrefixedTranslation } from 'hooks';
import { CLightningNode, LightningNode, LndNode, Status } from 'shared/types';
import { CLightningNode, LampoNode, LightningNode, LndNode, Status } from 'shared/types';
import { useStoreActions, useStoreState } from 'store';
import { ellipseInner } from 'utils/strings';
import { Loader } from 'components/common';
Expand Down Expand Up @@ -125,6 +125,15 @@ const ConnectTab: React.FC<Props> = ({ node }) => {
p2pUriExternal: `${pubkey}@127.0.0.1:${cln.ports.p2p}`,
authTypes: ['paths', 'hex', 'base64'],
};
} else if (node.implementation === 'lampo') {
const lampo = node as LampoNode;
return {
restUrl: `http://127.0.0.1:${lampo.ports.rest}`,
restDocsUrl: 'https://github.com/privkeyio/lampo.rs',
credentials: {},
p2pUriExternal: `${pubkey}@127.0.0.1:${lampo.ports.p2p}`,
authTypes: [],
};
}
}

Expand Down
36 changes: 34 additions & 2 deletions src/components/designer/lightning/LightningDetails.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';
import { shell } from 'electron';
import { fireEvent, waitFor } from '@testing-library/react';
import { LightningNode, Status } from 'shared/types';
import { LampoNode, LightningNode, Status } from 'shared/types';
import { Network } from 'types';
import * as files from 'utils/files';
import { createCLightningNetworkNode } from 'utils/network';
import { createCLightningNetworkNode, createLampoNetworkNode } from 'utils/network';
import {
defaultStateBalances,
defaultStateInfo,
Expand Down Expand Up @@ -362,6 +362,38 @@ describe('LightningDetails', () => {
});
});

describe('lampo', () => {
beforeEach(() => {
node = createLampoNetworkNode(network, '0.1.0-blake2b.1', undefined, {
image: '',
command: '',
});
network.nodes.lightning.push(node);
});

it('should display the REST Host and P2P address without credentials', async () => {
const lampo = node as LampoNode;
const { getByText, queryByText, findByText } = renderComponent(Status.Started);
fireEvent.click(await findByText('Connect'));
expect(getByText('REST Host')).toBeInTheDocument();
expect(getByText(`http://127.0.0.1:${lampo.ports.rest}`)).toBeInTheDocument();
expect(queryByText('GRPC Host')).not.toBeInTheDocument();
expect(queryByText('File Paths')).not.toBeInTheDocument();
});

it('should open the API Doc link in the browser', async () => {
shell.openExternal = jest.fn().mockResolvedValue(true);
const { getByText, findByText } = renderComponent(Status.Started);
fireEvent.click(await findByText('Connect'));
fireEvent.click(getByText('REST'));
await waitFor(() => {
expect(shell.openExternal).toBeCalledWith(
'https://github.com/privkeyio/lampo.rs',
);
});
});
});

describe('connect options', () => {
it('should not fail with undefined node state', async () => {
lightningServiceMock.getInfo.mockResolvedValue(undefined as any);
Expand Down
4 changes: 3 additions & 1 deletion src/components/designer/link/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ const Channel: React.FC<Props> = ({ link, from, to }) => {
{ label: l('sourceBalance'), value: `${format(fromBalance)} sats` },
{ label: l('destinationBalance'), value: `${format(toBalance)} sats` },
{
label: from.implementation === 'LND' ? l('channelPoint') : l('channelId'),
label: ['LND', 'lampo'].includes(from.implementation)
? l('channelPoint')
: l('channelId'),
value: (
<CopyIcon
value={channelPoint}
Expand Down
11 changes: 11 additions & 0 deletions src/components/network/ImportNetwork.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe('ImportNetwork component', () => {
description: 'network description',
lndNodes: 2,
clightningNodes: 1,
lampoNodes: 1,
bitcoindNodes: 1,
status: Status.Started,
repoState: defaultRepoState,
Expand Down Expand Up @@ -162,6 +163,16 @@ describe('ImportNetwork component', () => {
expect(await findByText(msg)).toBeInTheDocument();
});

it('should throw if a lampo node is imported on Windows', async () => {
osMock.platform.mockReturnValue('win32');
filesMock.read.mockResolvedValue(JSON.stringify({ network, chart }));
const { findByText, fileInput } = renderComponent();
fireEvent.change(fileInput);
expect(await findByText("Could not import 'file.zip'")).toBeInTheDocument();
const msg = 'Importing networks with lampo nodes is not supported on windows';
expect(await findByText(msg)).toBeInTheDocument();
});

it('should throw if a lightning node is not supported on the current platform', async () => {
osMock.platform.mockReturnValue('aix' as any);
filesMock.read.mockResolvedValue(JSON.stringify({ network, chart }));
Expand Down
1 change: 1 addition & 0 deletions src/components/network/NetworkSetting.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('NetworkSetting Component', () => {
const { getByLabelText, getAllByLabelText } = renderComponent();
expect(getAllByLabelText('LND')[0]).toHaveValue('8081');
expect(getAllByLabelText('Core Lightning')[0]).toHaveValue('8181');
expect(getByLabelText('Lampo')).toHaveValue('8281');
expect(getByLabelText('Bitcoin Knots')).toHaveValue('18443');
expect(getAllByLabelText('LND')[1]).toHaveValue('10001');
expect(getAllByLabelText('Core Lightning')[1]).toHaveValue('11001');
Expand Down
9 changes: 9 additions & 0 deletions src/components/network/NetworkSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ const NetworkSetting: React.FC = () => {
rest: values['c-lightning'],
grpc: values['grpcC-lightning'],
},
lampo: {
rest: values.lampo,
},
bitcoind: {
rest: values.bitcoind,
},
Expand Down Expand Up @@ -82,6 +85,7 @@ const NetworkSetting: React.FC = () => {
initialValues={{
LND: settings.basePorts.LND.rest,
'c-lightning': settings.basePorts['c-lightning'].rest,
lampo: settings.basePorts.lampo.rest,
bitcoind: settings.basePorts.bitcoind.rest,
grpcLND: settings.basePorts.LND.grpc,
'grpcC-lightning': settings.basePorts['c-lightning'].grpc,
Expand All @@ -101,6 +105,11 @@ const NetworkSetting: React.FC = () => {
<InputNumber />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item name="lampo" label={dockerConfigs.lampo.name}>
<InputNumber />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item name="bitcoind" label={dockerConfigs.bitcoind.name}>
<InputNumber />
Expand Down
9 changes: 9 additions & 0 deletions src/components/network/NewNetwork.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,19 @@ describe('NewNetwork component', () => {
const { getByLabelText, queryByText } = renderComponent();
expect(getByLabelText('LND')).toHaveValue('1');
expect(getByLabelText('Core Lightning')).toHaveValue('1');
expect(getByLabelText('Lampo')).toHaveValue('0');
expect(getByLabelText('Lampo')).toBeEnabled();
expect(getByLabelText('Bitcoin Knots')).toHaveValue('1');
expect(queryByText('My Test Image')).not.toBeInTheDocument();
});

it('should disable Lampo nodes on Windows', () => {
mockOS.platform.mockReturnValue('win32');
const { getByLabelText, getByText } = renderComponent();
expect(getByLabelText('Lampo')).toBeDisabled();
expect(getByText('Not supported on Windows yet.')).toBeInTheDocument();
});

it('should display custom nodes', () => {
const { getByLabelText } = renderComponent(true);
expect(getByLabelText('My Test Image')).toHaveValue('0');
Expand Down
12 changes: 12 additions & 0 deletions src/components/network/NewNetwork.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useTheme } from 'hooks/useTheme';
import { useStoreActions, useStoreState } from 'store';
import { ThemeColors } from 'theme/colors';
import { dockerConfigs } from 'utils/constants';
import { isWindows } from 'utils/system';
import { HOME } from 'components/routing';

const Styled = {
Expand Down Expand Up @@ -76,6 +77,7 @@ const NewNetwork: React.FC = () => {
initialValues={{
lndNodes: settings.newNodeCounts.LND,
clightningNodes: settings.newNodeCounts['c-lightning'],
lampoNodes: settings.newNodeCounts.lampo,
bitcoindNodes: settings.newNodeCounts.bitcoind,
customNodes: initialCustomValues,
}}
Expand Down Expand Up @@ -133,6 +135,16 @@ const NewNetwork: React.FC = () => {
<InputNumber min={0} max={10} />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
name="lampoNodes"
label={dockerConfigs.lampo.name}
extra={isWindows() ? l('lampoWindows') : ''}
rules={[{ required: true, message: l('cmps.forms.required') }]}
>
<InputNumber min={0} max={10} disabled={isWindows()} />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
name="bitcoindNodes"
Expand Down
2 changes: 1 addition & 1 deletion src/components/nodeImages/CustomImageModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const CustomImageModal: React.FC<Props> = ({ image, onClose }) => {
};

const platform = getPolarPlatform();
const lnImpls: NodeImplementation[] = ['LND', 'c-lightning'];
const lnImpls: NodeImplementation[] = ['LND', 'c-lightning', 'lampo'];
const implGroups: Record<string, NodeImplementation[]> = {
Lightning: lnImpls.filter(i => dockerConfigs[i].platforms.includes(platform)),
Bitcoin: ['bitcoind'],
Expand Down
Loading
Loading