Merge fort-nix/nix-bitcoin#688: rtl: 0.14.1 -> 0.15.4, add clightning.plugins.clnrest

daa3bfbae3 lndconnect: add clnrest (Erik Arvstedt)
87b929bc99 wireguard-lndconnect: remove obsolete workaround (Erik Arvstedt)
d682cb95c8 README: remove `clightning-rest` (Erik Arvstedt)
ad2a128471 rtl: use clnrest (Erik Arvstedt)
4e9c153f9c rtl: 0.14.1 -> 0.15.4 (Erik Arvstedt)
e74ddd9464 clightning.plugins.clnrest: init (Erik Arvstedt)

Pull request description:

ACKs for top commit:
  jonasnick:
    ACK daa3bfbae3

Tree-SHA512: ce6994977287998f41134d445824e8d4a162715d94604704b8706a7f27947aae98e97de296977d6e392df9300d71dc44e2c1c77a6f99ca515d99dc93b4f7eb91
This commit is contained in:
Jonas Nick 2024-11-27 21:16:22 +00:00
commit 64fb83b742
No known key found for this signature in database
GPG key ID: 4861DBF262123605
22 changed files with 347 additions and 60 deletions

View file

@ -78,7 +78,6 @@ NixOS modules ([src](modules/modules.nix))
* [rebalance](https://github.com/lightningd/plugins/tree/master/rebalance): keeps your channels balanced * [rebalance](https://github.com/lightningd/plugins/tree/master/rebalance): keeps your channels balanced
* [trustedcoin](https://github.com/nbd-wtf/trustedcoin) ([experimental](docs/services.md#trustedcoin)): replaces bitcoind with trusted public explorers * [trustedcoin](https://github.com/nbd-wtf/trustedcoin) ([experimental](docs/services.md#trustedcoin)): replaces bitcoind with trusted public explorers
* [zmq](https://github.com/lightningd/plugins/tree/master/zmq): publishes notifications via ZeroMQ to configured endpoints * [zmq](https://github.com/lightningd/plugins/tree/master/zmq): publishes notifications via ZeroMQ to configured endpoints
* [clightning-rest](https://github.com/Ride-The-Lightning/c-lightning-REST): REST server for clightning
* [lnd](https://github.com/lightningnetwork/lnd) with support for announcing an onion service and [static channel backups](https://github.com/lightningnetwork/lnd/blob/master/docs/recovery.md) * [lnd](https://github.com/lightningnetwork/lnd) with support for announcing an onion service and [static channel backups](https://github.com/lightningnetwork/lnd/blob/master/docs/recovery.md)
* [Lightning Loop](https://github.com/lightninglabs/loop) * [Lightning Loop](https://github.com/lightninglabs/loop)
* [Lightning Pool](https://github.com/lightninglabs/pool) * [Lightning Pool](https://github.com/lightninglabs/pool)

View file

@ -66,6 +66,16 @@ with lib;
onion = true; onion = true;
}; };
}; };
services.clightning = {
enable = true;
plugins.clnrest = {
enable = true;
lnconnect = {
enable = true;
onion = true;
};
};
};
services.clightning-rest = { services.clightning-rest = {
enable = true; enable = true;
lndconnect = { lndconnect = {

View file

@ -7,13 +7,15 @@ run-tests.sh -s wireguard-lndconnect-online container
# 2. Test connecting via Tor # 2. Test connecting via Tor
# Print QR codes for lnd, clightning-rest connections via Tor # Print QR codes for lnd, clightning-rest connections via Tor
c lndconnect c lndconnect
c lnconnect-clnrest
c lndconnect-clightning c lndconnect-clightning
# Add these to Zeus >= 0.7.1. # Add these to Zeus >= 0.9.0.
# To explicitly check if the connection is successful, press the node logo in the top # To explicitly check if the connection is successful, press the node logo in the top
# left corner, and then "Node Info". # left corner, and then "Node Info".
# Debug # Debug
c lndconnect --url c lndconnect --url
c lnconnect-clnrest --url
c lndconnect-clightning --url c lndconnect-clightning --url
# 3. Test connecting via WireGuard # 3. Test connecting via WireGuard
@ -33,13 +35,15 @@ c nix-bitcoin-wg-connect --text
# Print QR codes for lnd, clightning-rest connections via WireGuard # Print QR codes for lnd, clightning-rest connections via WireGuard
c lndconnect-wg c lndconnect-wg
c lnconnect-clnrest-wg
c lndconnect-clightning-wg c lndconnect-clightning-wg
# Add these to Zeus >= 0.7.1. # Add these to Zeus >= 0.9.0.
# To explicitly check if the connection is successful, press the node logo in the top # To explicitly check if the connection is successful, press the menu button in the top
# left corner, and then "Node Info". # left corner, and then "Node Info".
# Debug # Debug
c lndconnect-wg --url c lndconnect-wg --url
c lnconnect-clnrest-wg --url
c lndconnect-clightning-wg --url c lndconnect-clightning-wg --url
# 3.3.remove external firewall port forward, remove local port forward: # 3.3.remove external firewall port forward, remove local port forward:
@ -55,6 +59,8 @@ c nodeinfo
c lndconnect --url c lndconnect --url
c lndconnect-wg --url c lndconnect-wg --url
c lndconnect-clnrest --url
c lndconnect-clnrest-wg --url
c lndconnect-clightning --url c lndconnect-clightning --url
c lndconnect-clightning-wg --url c lndconnect-clightning-wg --url

View file

@ -17,8 +17,6 @@ c systemctl status rtl
c journalctl -u rtl c journalctl -u rtl
c cat /var/lib/rtl/RTL-Config.json c cat /var/lib/rtl/RTL-Config.json
c systemctl status clightning-rest
# Open webinterface. Password: a # Open webinterface. Password: a
runuser -u "$(logname)" -- xdg-open "http://$ip:3000" runuser -u "$(logname)" -- xdg-open "http://$ip:3000"

View file

@ -143,7 +143,7 @@ The default password location is `$secretsDir/rtl-password`.
See: [Secrets dir](./configuration.md#secrets-dir) See: [Secrets dir](./configuration.md#secrets-dir)
# Use Zeus (mobile lightning wallet) via Tor # Use Zeus (mobile lightning wallet) via Tor
1. Install [Zeus](https://zeusln.app) (version ≥ 0.7.1) 1. Install [Zeus](https://zeusln.app) (version ≥ 0.9.0)
2. Edit your `configuration.nix` 2. Edit your `configuration.nix`
@ -161,9 +161,9 @@ See: [Secrets dir](./configuration.md#secrets-dir)
Add the following config: Add the following config:
```nix ```nix
services.clightning-rest = { services.clightning.plugins.clnrest = {
enable = true; enable = true;
lndconnect = { lnconnect = {
enable = true; enable = true;
onion = true; onion = true;
}; };
@ -182,7 +182,7 @@ See: [Secrets dir](./configuration.md#secrets-dir)
##### For clightning ##### For clightning
``` ```
lndconnect-clightning lnconnect-clnrest
``` ```
5. Configure Zeus 5. Configure Zeus
@ -212,7 +212,7 @@ There are two ways to establish a secure, direct connection:
- Connecting via WireGuard. This approach is simpler and more versatile, and is - Connecting via WireGuard. This approach is simpler and more versatile, and is
described in this guide. described in this guide.
1. Install [Zeus](https://zeusln.app) (version ≥ 0.7.1) and 1. Install [Zeus](https://zeusln.app) (version ≥ 0.9.0) and
[WireGuard](https://www.wireguard.com/install/) on your mobile device. [WireGuard](https://www.wireguard.com/install/) on your mobile device.
2. Add the following to your `configuration.nix`: 2. Add the following to your `configuration.nix`:
@ -229,9 +229,11 @@ There are two ways to establish a secure, direct connection:
services.lnd.lndconnect.enable = true; services.lnd.lndconnect.enable = true;
# For clightning # For clightning
services.clightning-rest = { services.clightning = {
plugins.clnrest = {
enable = true; enable = true;
lndconnect.enable = true; lnconnect.enable = true;
};
}; };
``` ```
3. Deploy your configuration. 3. Deploy your configuration.
@ -275,7 +277,7 @@ There are two ways to establish a secure, direct connection:
##### For clightning ##### For clightning
``` ```
lndconnect-clightning-wg lnconnect-clnrest-wg
``` ```
Configure Zeus: Configure Zeus:

View file

@ -56,15 +56,15 @@
# #
# == REST server # == REST server
# Set this to create a clightning REST onion service. # Set this to create a clightning REST onion service.
# This also adds binary `lndconnect-clightning` to the system environment. # This also adds binary `lnconnect-clnrest` to the system environment.
# This binary creates QR codes or URLs for connecting applications to clightning # This binary creates QR codes or URLs for connecting applications to clightning
# via the REST onion service. # via the REST onion service.
# You can also connect via WireGuard instead of Tor. # You can also connect via WireGuard instead of Tor.
# See ../docs/services.md for details. # See ../docs/services.md for details.
# #
# services.clightning-rest = { # services.clightning.plugins.clnrest = {
# enable = true; # enable = true;
# lndconnect = { # lnconnect = {
# enable = true; # enable = true;
# onion = true; # onion = true;
# }; # };

View file

@ -0,0 +1,78 @@
{ config, lib, pkgs, ... }:
with lib;
let
options = {
services.clightning.plugins.clnrest = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable clnrest (clightning plugin).
clnrest provides a clightning REST API, using clightning RPC calls as its backend.
It also broadcasts clightning notifications to listeners connected to its websocket server.
See here for all available options:
https://docs.corelightning.org/docs/rest
Extra options can be set via `services.clightning.extraConfig`.
'';
};
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Address to listen for REST connections.";
};
port = mkOption {
type = types.port;
default = 3010;
description = "REST server port.";
};
createAdminRune = mkOption {
type = types.bool;
default = true;
description = ''
Create a rune with admin permissions at path `''${config.services.clightning.networkDir}/admin-rune`.
'';
};
package = mkOption {
type = types.package;
default = config.nix-bitcoin.pkgs.nbPython3Packages.clnrest;
defaultText = "config.nix-bitcoin.pkgs.nbPython3Packages.clnrest";
description = "The package providing clnrest binaries.";
};
};
# Internal read-only options used by `./nodeinfo.nix` and `./onion-services.nix`
services.clnrest = let
inherit (config.nix-bitcoin.lib) mkAlias;
in {
enable = mkAlias cfg.enable;
address = mkAlias cfg.address;
port = mkAlias cfg.port;
};
};
cfg = config.services.clightning.plugins.clnrest;
inherit (config.services) clightning;
runePath = "${clightning.networkDir}/admin-rune";
in
{
inherit options;
config = mkIf cfg.enable {
services.clightning.extraConfig = ''
plugin=${cfg.package}/bin/clnrest
clnrest-host=${cfg.address}
clnrest-port=${toString cfg.port}
'';
systemd.services.clightning.postStart = mkIf cfg.createAdminRune (mkAfter ''
if [[ ! -e '${runePath}' ]]; then
rune=$(${clightning.cli}/bin/lightning-cli createrune | ${pkgs.jq}/bin/jq -r .rune)
install -m 640 <(echo "$rune") '${runePath}'
fi
'');
};
}

View file

@ -13,6 +13,7 @@ let
in { in {
imports = [ imports = [
./clboss.nix ./clboss.nix
./clnrest.nix
./feeadjuster.nix ./feeadjuster.nix
./trustedcoin.nix ./trustedcoin.nix
./zmq.nix ./zmq.nix

View file

@ -32,6 +32,34 @@ let
}; };
}; };
services.clightning.plugins.clnrest.lnconnect = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Add a `lnconnect-clnrest` binary to the system environment which prints
connection info for clightning clients.
See: https://github.com/LN-Zap/lndconnect
Usage:
```bash
# Print QR code
lnconnect-clnrest
# Print URL
lnconnect-clnrest --url
```
'';
};
onion = mkOption {
type = types.bool;
default = false;
description = ''
Create an onion service for the clnrest server,
which is used by lnconnect.
'';
};
};
services.clightning-rest.lndconnect = { services.clightning-rest.lndconnect = {
enable = mkOption { enable = mkOption {
@ -77,14 +105,18 @@ let
inherit (config.services) inherit (config.services)
lnd lnd
clightning
clightning-rest; clightning-rest;
inherit (clightning.plugins) clnrest;
mkLndconnect = { mkLndconnect = {
name, name,
shebang ? "#!${pkgs.stdenv.shell} -e", shebang ? "#!${pkgs.stdenv.shell} -e",
isClightning ? false, isClightning ? false,
isClnrest ? false,
port, port,
macaroonPath, authSecretPath,
enableOnion, enableOnion,
onionService ? null, onionService ? null,
certPath ? null certPath ? null
@ -99,7 +131,7 @@ let
${optionalString enableOnion "--host=$(cat ${config.nix-bitcoin.onionAddresses.dataDir}/${onionService})"} \ ${optionalString enableOnion "--host=$(cat ${config.nix-bitcoin.onionAddresses.dataDir}/${onionService})"} \
--port=${toString port} \ --port=${toString port} \
${if enableOnion || certPath == null then "--nocert" else "--tlscertpath='${certPath}'"} \ ${if enableOnion || certPath == null then "--nocert" else "--tlscertpath='${certPath}'"} \
--adminmacaroonpath='${macaroonPath}' \ --adminmacaroonpath='${authSecretPath}' \
--configfile=/dev/null "$@" --configfile=/dev/null "$@"
) )
@ -109,7 +141,7 @@ let
# Because `macaroon` is always the last URL fragment, the # Because `macaroon` is always the last URL fragment, the
# sed replacement below works correctly. # sed replacement below works correctly.
'' ''
macaroonHex=$(${getExe pkgs.xxd} -p -u -c 99999 '${macaroonPath}') macaroonHex=$(${getExe pkgs.xxd} -p -u -c 99999 '${authSecretPath}')
url=$( url=$(
echo "$url" | ${getExe pkgs.gnused} " echo "$url" | ${getExe pkgs.gnused} "
s|^lndconnect|c-lightning-rest| s|^lndconnect|c-lightning-rest|
@ -119,6 +151,18 @@ let
'' ''
} }
${optionalString isClnrest
# Change URL procotcol to clnrest
''
url=$(
echo "$url" | ${getExe pkgs.gnused} "
s|^lndconnect|clnrest|
s|macaroon=.*|rune=$(cat '${authSecretPath}')|
";
)
''
}
# If --url is in args # If --url is in args
if [[ " $* " =~ " --url " ]]; then if [[ " $* " =~ " --url " ]]; then
echo "$url" echo "$url"
@ -146,7 +190,7 @@ in {
onionService = "${lnd.user}/lnd-rest"; onionService = "${lnd.user}/lnd-rest";
port = lnd.restPort; port = lnd.restPort;
certPath = lnd.certPath; certPath = lnd.certPath;
macaroonPath = "${lnd.networkDir}/admin.macaroon"; authSecretPath = "${lnd.networkDir}/admin.macaroon";
} }
)]; )];
@ -169,6 +213,39 @@ in {
}) })
])) ]))
(mkIf (clnrest.enable && clnrest.lnconnect.enable)
(mkMerge [
{
environment.systemPackages = [(
mkLndconnect {
name = "lnconnect-clnrest";
isClnrest = true;
enableOnion = clnrest.lnconnect.onion;
onionService = "${operatorName}/clnrest";
port = clnrest.port;
certPath = "${clightning.networkDir}/client.pem";
authSecretPath = "${clightning.networkDir}/admin-rune";
}
)];
services.clightning.plugins.clnrest.address = mkIf (!clnrest.lnconnect.onion) "0.0.0.0";
}
(mkIf clnrest.lnconnect.onion {
services.tor = {
enable = true;
relay.onionServices.clnrest = nbLib.mkOnionService {
target.addr = nbLib.address clnrest.address;
target.port = clnrest.port;
port = clnrest.port;
};
};
# This also allows nodeinfo to show the clnrest onion address
nix-bitcoin.onionAddresses.access.${operatorName} = [ "clnrest" ];
})
])
)
(mkIf (clightning-rest.enable && clightning-rest.lndconnect.enable) (mkIf (clightning-rest.enable && clightning-rest.lndconnect.enable)
(mkMerge [ (mkMerge [
{ {
@ -180,7 +257,7 @@ in {
onionService = "${operatorName}/clightning-rest"; onionService = "${operatorName}/clightning-rest";
port = clightning-rest.port; port = clightning-rest.port;
certPath = "${clightning-rest.dataDir}/certs/certificate.pem"; certPath = "${clightning-rest.dataDir}/certs/certificate.pem";
macaroonPath = "${clightning-rest.dataDir}/certs/access.macaroon"; authSecretPath = "${clightning-rest.dataDir}/certs/access.macaroon";
} }
)]; )];

View file

@ -138,11 +138,7 @@ let
# Internal read-only options used by `./nodeinfo.nix` and `./onion-services.nix` # Internal read-only options used by `./nodeinfo.nix` and `./onion-services.nix`
mempool-frontend = let mempool-frontend = let
mkAlias = default: mkOption { inherit (nbLib) mkAlias;
internal = true;
readOnly = true;
inherit default;
};
in { in {
enable = mkAlias cfg.frontend.enable; enable = mkAlias cfg.frontend.enable;
address = mkAlias cfg.frontend.address; address = mkAlias cfg.frontend.address;

View file

@ -286,7 +286,7 @@ in {
in in
optional nodes.lnd.enable "lnd" ++ optional nodes.lnd.enable "lnd" ++
optional (nodes.lnd.enable && nodes.lnd.loop) "lightning-loop" ++ optional (nodes.lnd.enable && nodes.lnd.loop) "lightning-loop" ++
optional nodes.clightning.enable "clightning-rest"; optional nodes.clightning.enable "clightning";
}; };
clightning-rest = { clightning-rest = {
id = 30; id = 30;
@ -315,6 +315,7 @@ in {
}; };
services.clightning.address = netns.clightning.address; services.clightning.address = netns.clightning.address;
services.clightning.plugins.clnrest.address = netns.clightning.address;
services.lnd = { services.lnd = {
address = netns.lnd.address; address = netns.lnd.address;

View file

@ -142,6 +142,10 @@ in {
'') + '' '') + ''
info["nodeid"] = shell("lncli getinfo | jq -r '.identity_pubkey'") info["nodeid"] = shell("lncli getinfo | jq -r '.identity_pubkey'")
'') name cfg; '') name cfg;
clnrest = name: cfg: mkInfoLong {
inherit name cfg;
systemdServiceName = "clightning";
};
clightning-rest = mkInfo ""; clightning-rest = mkInfo "";
electrs = mkInfo ""; electrs = mkInfo "";
fulcrum = mkInfo ""; fulcrum = mkInfo "";
@ -153,7 +157,6 @@ in {
mempool-frontend = name: cfg: mkInfoLong { mempool-frontend = name: cfg: mkInfoLong {
inherit name cfg; inherit name cfg;
systemdServiceName = "nginx"; systemdServiceName = "nginx";
extraCode = "";
}; };
# Only add sshd when it has an onion service # Only add sshd when it has an onion service
sshd = name: cfg: mkIfOnionPort "sshd" (onionPort: '' sshd = name: cfg: mkIfOnionPort "sshd" (onionPort: ''

View file

@ -2,7 +2,8 @@
# Create a WireGuard server with a single peer. # Create a WireGuard server with a single peer.
# Private/public keys are created via the secrets system. # Private/public keys are created via the secrets system.
# Add helper binaries `nix-bitcoin-wg-connect` and optionally `lndconnect-wg`, `lndconnect-clightning-wg`. # Add helper binaries `nix-bitcoin-wg-connect` and optionally `lndconnect-wg`,
# `lnconnect-clnrest-wg`, `lndconnect-clightning-wg`.
# See ../../docs/services.md ("Use Zeus (mobile lightning wallet) via WireGuard") # See ../../docs/services.md ("Use Zeus (mobile lightning wallet) via WireGuard")
# for usage instructions. # for usage instructions.
@ -33,9 +34,12 @@ let
inherit (config.networking.wireguard.interfaces) wg-nb; inherit (config.networking.wireguard.interfaces) wg-nb;
inherit (config.services) inherit (config.services)
lnd lnd
clightning
clightning-rest; clightning-rest;
inherit (clightning.plugins) clnrest;
lndconnect = lnd.enable && lnd.lndconnect.enable; lndconnect = lnd.enable && lnd.lndconnect.enable;
lnconnect-clnrest = clnrest.enable && clnrest.lnconnect.enable;
lndconnect-clightning = clightning-rest.enable && clightning-rest.lndconnect.enable; lndconnect-clightning = clightning-rest.enable && clightning-rest.lndconnect.enable;
serverAddress = "${wgSubnet}.1"; serverAddress = "${wgSubnet}.1";
@ -150,6 +154,10 @@ in {
(pkgs.writers.writeBashBin "lndconnect-wg" '' (pkgs.writers.writeBashBin "lndconnect-wg" ''
exec lndconnect --host "${serverAddress}" --nocert "$@" exec lndconnect --host "${serverAddress}" --nocert "$@"
'') '')
) ++ (optional lnconnect-clnrest
(pkgs.writers.writeBashBin "lnconnect-clnrest-wg" ''
exec lnconnect-clnrest --host "${serverAddress}" --nocert "$@"
'')
) ++ (optional lndconnect-clightning ) ++ (optional lndconnect-clightning
(pkgs.writers.writeBashBin "lndconnect-clightning-wg" '' (pkgs.writers.writeBashBin "lndconnect-clightning-wg" ''
exec lndconnect-clightning --host "${serverAddress}" --nocert "$@" exec lndconnect-clightning --host "${serverAddress}" --nocert "$@"
@ -165,6 +173,9 @@ in {
optionalString lndconnect '' optionalString lndconnect ''
iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString lnd.restPort} -j nixos-fw-accept iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString lnd.restPort} -j nixos-fw-accept
'' ''
+ optionalString lnconnect-clnrest ''
iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString clnrest.port} -j nixos-fw-accept
''
+ optionalString lndconnect-clightning '' + optionalString lndconnect-clightning ''
iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString clightning-rest.port} -j nixos-fw-accept iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString clightning-rest.port} -j nixos-fw-accept
'' ''
@ -187,6 +198,11 @@ in {
restAddress = "0.0.0.0"; restAddress = "0.0.0.0";
tor.enforce = false; tor.enforce = false;
}; };
services.clightning.plugins.clnrest.address = mkIf lnconnect-clnrest "0.0.0.0";
# clnrest runs inside `clightning.service`
services.clightning.tor.enforce = mkIf lnconnect-clnrest false;
services.clightning-rest = mkIf lndconnect-clightning { services.clightning-rest = mkIf lndconnect-clightning {
# clightning-rest always listens on "0.0.0.0" # clightning-rest always listens on "0.0.0.0"
tor.enforce = false; tor.enforce = false;

View file

@ -107,6 +107,7 @@ let
nbLib = config.nix-bitcoin.lib; nbLib = config.nix-bitcoin.lib;
nbPkgs = config.nix-bitcoin.pkgs; nbPkgs = config.nix-bitcoin.pkgs;
secretsDir = config.nix-bitcoin.secretsDir; secretsDir = config.nix-bitcoin.secretsDir;
runePath = "${cfg.dataDir}/clightning-admin-rune";
inherit (nbLib) optionalAttr; inherit (nbLib) optionalAttr;
@ -116,9 +117,8 @@ let
lnImplementation = if isLnd then "LND" else "CLT"; lnImplementation = if isLnd then "LND" else "CLT";
Authentication = { Authentication = {
${optionalAttr (isLnd && lndLoopEnabled) "swapMacaroonPath"} = "${lightning-loop.dataDir}/${bitcoind.network}"; ${optionalAttr (isLnd && lndLoopEnabled) "swapMacaroonPath"} = "${lightning-loop.dataDir}/${bitcoind.network}";
macaroonPath = if isLnd ${optionalAttr (isLnd) "macaroonPath"} = "${cfg.dataDir}/macaroons";
then "${cfg.dataDir}/macaroons" ${optionalAttr (!isLnd) "runePath"} = runePath;
else "${clightning-rest.dataDir}/certs";
}; };
Settings = { Settings = {
userPersona = "OPERATOR"; userPersona = "OPERATOR";
@ -133,7 +133,7 @@ let
lnServerUrl = "https://${ lnServerUrl = "https://${
if isLnd if isLnd
then nbLib.addressWithPort lnd.restAddress lnd.restPort then nbLib.addressWithPort lnd.restAddress lnd.restPort
else nbLib.addressWithPort clightning-rest.address clightning-rest.port else nbLib.addressWithPort clightning.plugins.clnrest.address clightning.plugins.clnrest.port
}"; }";
}; };
}; };
@ -159,7 +159,7 @@ let
inherit (config.services) inherit (config.services)
bitcoind bitcoind
lnd lnd
clightning-rest clightning
lightning-loop; lightning-loop;
lndLoopEnabled = cfg.nodes.lnd.enable && cfg.nodes.lnd.loop; lndLoopEnabled = cfg.nodes.lnd.enable && cfg.nodes.lnd.loop;
@ -177,7 +177,10 @@ in {
services.lnd.enable = mkIf cfg.nodes.lnd.enable true; services.lnd.enable = mkIf cfg.nodes.lnd.enable true;
services.lightning-loop.enable = mkIf lndLoopEnabled true; services.lightning-loop.enable = mkIf lndLoopEnabled true;
services.clightning-rest.enable = mkIf cfg.nodes.clightning.enable true; services.clightning = mkIf cfg.nodes.clightning.enable {
enable = true;
plugins.clnrest.enable = true;
};
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
@ -187,7 +190,7 @@ in {
systemd.services.rtl = rec { systemd.services.rtl = rec {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = optional cfg.nodes.clightning.enable "clightning-rest.service" ++ requires = optional cfg.nodes.clightning.enable "clightning.service" ++
optional cfg.nodes.lnd.enable "lnd.service"; optional cfg.nodes.lnd.enable "lnd.service";
after = requires ++ [ "nix-bitcoin-secrets.target" ]; after = requires ++ [ "nix-bitcoin-secrets.target" ];
environment.RTL_CONFIG_PATH = cfg.dataDir; environment.RTL_CONFIG_PATH = cfg.dataDir;
@ -198,10 +201,17 @@ in {
<${configFile} sed "s|@multiPass@|$(cat ${secretsDir}/rtl-password)|" \ <${configFile} sed "s|@multiPass@|$(cat ${secretsDir}/rtl-password)|" \
> '${cfg.dataDir}/RTL-Config.json' > '${cfg.dataDir}/RTL-Config.json'
'') '')
] ++ optional cfg.nodes.lnd.enable ]
++ optional cfg.nodes.lnd.enable
# The lnd admin macaroon is not readable by group `lnd`, so copy it
(nbLib.rootScript "rtl-copy-macaroon" '' (nbLib.rootScript "rtl-copy-macaroon" ''
install -D -o ${cfg.user} -g ${cfg.group} ${lnd.networkDir}/admin.macaroon \ install --compare -m 640 -o ${cfg.user} -g ${cfg.group} -D ${lnd.networkDir}/admin.macaroon \
'${cfg.dataDir}/macaroons/admin.macaroon' '${cfg.dataDir}/macaroons/admin.macaroon'
'')
++ optional cfg.nodes.clightning.enable
(nbLib.rootScript "rtl-create-clnrest-rune-file" ''
rune=$(cat '${clightning.networkDir}/admin-rune')
install --compare -m 640 -o ${cfg.user} -g ${cfg.group} <(printf 'LIGHTNING_RUNE="%s"\n' "$rune") '${runePath}'
''); '');
ExecStart = "${nbPkgs.rtl}/bin/rtl"; ExecStart = "${nbPkgs.rtl}/bin/rtl";
# Show "rtl" instead of "node" in the journal # Show "rtl" instead of "node" in the journal
@ -217,10 +227,7 @@ in {
users.users.${cfg.user} = { users.users.${cfg.user} = {
isSystemUser = true; isSystemUser = true;
group = cfg.group; group = cfg.group;
extraGroups = extraGroups = optional lndLoopEnabled lnd.group;
# Reads cert and macaroon from the clightning-rest datadir
optional cfg.nodes.clightning.enable clightning-rest.group ++
optional lndLoopEnabled lnd.group;
}; };
users.groups.${cfg.group} = {}; users.groups.${cfg.group} = {};

View file

@ -123,4 +123,10 @@ let self = {
mkIfTest = test: mkIf (config.tests.${test} or false); mkIfTest = test: mkIf (config.tests.${test} or false);
}; };
mkAlias = default: mkOption {
internal = true;
readOnly = true;
inherit default;
};
}; in self }; in self

View file

@ -0,0 +1,58 @@
{ buildPythonPackage
, clightning
, python
, poetry-core
, flask
, flask-cors
, flask-restx
, flask-socketio
, gevent
, gevent-websocket
, gunicorn
, pyln-client
, json5
, jsonschema
}:
let
self = buildPythonPackage rec {
pname = "clnrest";
version = clightning.version;
format = "pyproject";
inherit (clightning) src;
postUnpack = "sourceRoot=$sourceRoot/plugins/clnrest";
postPatch = ''
substituteInPlace pyproject.toml \
--replace 'gevent = "^23.9.0.post1"' 'gevent = "24.2.1"' \
--replace 'flask = "^2.3.3"' 'flask = "3.0.3"'
# Add extra required src files that are missing in pyproject.toml
sed -i '/authors/a include = [ { path = "utilities", format = ["sdist", "wheel"] } ]' pyproject.toml
'';
nativeBuildInputs = [ poetry-core ];
# From https://github.com/ElementsProject/lightning/blob/master/plugins/clnrest/pyproject.toml
propagatedBuildInputs = [
flask
flask-cors
flask-restx
flask-socketio
gevent
gevent-websocket
gunicorn
json5
pyln-client
];
postInstall = ''
makeWrapper ${python}/bin/python $out/bin/clnrest \
--set NIX_PYTHONPATH ${python.pkgs.makePythonPath self.propagatedBuildInputs} \
--add-flags "$out/lib/${python.libPrefix}/site-packages/clnrest.py"
'';
};
in
self

View file

@ -10,6 +10,7 @@ rec {
pyln-proto = clightningPkg ./pyln-proto; pyln-proto = clightningPkg ./pyln-proto;
pyln-bolt7 = clightningPkg ./pyln-bolt7; pyln-bolt7 = clightningPkg ./pyln-bolt7;
pylightning = clightningPkg ./pylightning; pylightning = clightningPkg ./pylightning;
clnrest = clightningPkg ./clnrest;
# Packages only used by joinmarket # Packages only used by joinmarket
bencoderpyx = callPackage ./bencoderpyx {}; bencoderpyx = callPackage ./bencoderpyx {};

View file

@ -9,11 +9,11 @@
}: }:
let self = stdenvNoCC.mkDerivation { let self = stdenvNoCC.mkDerivation {
pname = "rtl"; pname = "rtl";
version = "0.14.1"; version = "0.15.2";
src = fetchurl { src = fetchurl {
url = "https://github.com/Ride-The-Lightning/RTL/archive/refs/tags/v${self.version}.tar.gz"; url = "https://github.com/Ride-The-Lightning/RTL/archive/refs/tags/v${self.version}.tar.gz";
hash = "sha256-sbV7d/imdCXglpAS3hh7fETvSxMzegi63AfbS1imqbk="; hash = "sha256-8Scgrr1An8vwiVgDFxVP6FQksqd6ctwZN2W2ErPp39Y=";
}; };
passthru = { passthru = {
@ -25,7 +25,7 @@ let self = stdenvNoCC.mkDerivation {
# TODO-EXTERNAL: Remove `npmFlags` when no longer required # TODO-EXTERNAL: Remove `npmFlags` when no longer required
# See: https://github.com/Ride-The-Lightning/RTL/issues/1182 # See: https://github.com/Ride-The-Lightning/RTL/issues/1182
npmFlags = "--legacy-peer-deps"; npmFlags = "--legacy-peer-deps";
hash = "sha256-0fu14j4OvsYGBhu/p67EUFmuHCbIPlLVm4e8qd9tk3o="; hash = "sha256-LIKZOHc8FqvVX5LCJrq9Z76ZoyXLYiYsPIj6IieBWho=";
}; };
}; };

View file

@ -2,7 +2,7 @@
set -euo pipefail set -euo pipefail
. "${BASH_SOURCE[0]%/*}/../../helper/run-in-nix-env" "gnupg wget gnused" "$@" . "${BASH_SOURCE[0]%/*}/../../helper/run-in-nix-env" "gnupg wget gnused" "$@"
version="0.14.1" version="0.15.2"
repo=https://github.com/Ride-The-Lightning/RTL repo=https://github.com/Ride-The-Lightning/RTL
scriptDir=$(cd "${BASH_SOURCE[0]%/*}" && pwd) scriptDir=$(cd "${BASH_SOURCE[0]%/*}" && pwd)

View file

@ -64,6 +64,7 @@ let
nbPkgs = config.nix-bitcoin.pkgs; nbPkgs = config.nix-bitcoin.pkgs;
pluginPkgs = nbPkgs.clightning-plugins // { pluginPkgs = nbPkgs.clightning-plugins // {
clboss.path = "${plugins.clboss.package}/bin/clboss"; clboss.path = "${plugins.clboss.package}/bin/clboss";
clnrest.path = "${plugins.clnrest.package}/bin/clnrest";
trustedcoin.path = "${plugins.trustedcoin.package}/bin/trustedcoin"; trustedcoin.path = "${plugins.trustedcoin.package}/bin/trustedcoin";
}; };
in map (plugin: pluginPkgs.${plugin}.path) enabled; in map (plugin: pluginPkgs.${plugin}.path) enabled;
@ -102,6 +103,7 @@ let
nix-bitcoin.onionServices.lnd.public = true; nix-bitcoin.onionServices.lnd.public = true;
tests.lndconnect-onion-lnd = with cfg.lnd.lndconnect; enable && onion; tests.lndconnect-onion-lnd = with cfg.lnd.lndconnect; enable && onion;
tests.lnconnect-onion-clnrest = with cfg.clightning.plugins.clnrest.lnconnect; enable && onion;
tests.lndconnect-onion-clightning = with cfg.clightning-rest.lndconnect; enable && onion; tests.lndconnect-onion-clightning = with cfg.clightning-rest.lndconnect; enable && onion;
tests.lightning-loop = cfg.lightning-loop.enable; tests.lightning-loop = cfg.lightning-loop.enable;
@ -194,6 +196,10 @@ let
encrypt = true; encrypt = true;
local.directory = "/var/backup/clightning"; local.directory = "/var/backup/clightning";
}; };
services.clightning.plugins.clnrest = {
enable = true;
lnconnect = { enable = true; onion = true; };
};
test.features.clightningPlugins = true; test.features.clightningPlugins = true;
services.rtl.enable = true; services.rtl.enable = true;
services.clightning-rest.enable = true; services.clightning-rest.enable = true;

View file

@ -179,6 +179,11 @@ def _():
assert_running("lnd") assert_running("lnd")
assert_matches("runuser -u operator -- lndconnect --url", ".onion") assert_matches("runuser -u operator -- lndconnect --url", ".onion")
@test("lnconnect-onion-clnrest")
def _():
assert_running("clightning")
assert_matches("runuser -u operator -- lnconnect-clnrest --url", ".onion")
@test("lndconnect-onion-clightning") @test("lndconnect-onion-clightning")
def _(): def _():
assert_running("clightning-rest") assert_running("clightning-rest")

View file

@ -18,18 +18,22 @@ makeTestVM {
nix-bitcoin.generateSecrets = true; nix-bitcoin.generateSecrets = true;
nix-bitcoin.operator.enable = true; nix-bitcoin.operator.enable = true;
services.clightning = {
enable = true;
plugins.clnrest = {
enable = true;
lnconnect.enable = true;
};
};
services.clightning-rest = { services.clightning-rest = {
enable = true; enable = true;
lndconnect.enable = true; lndconnect.enable = true;
}; };
# TODO-EXTERNAL: # TODO-EXTERNAL:
# When WAN is disabled, DNS bootstrapping slows down service startup by ~15 s. # When WAN is disabled, DNS bootstrapping slows down service startup by ~15 s.
# TODO-EXTERNAL:
# When bitcoind is not fully synced, the offers plugin in clightning 24.05
# crashes (see https://github.com/ElementsProject/lightning/issues/7378).
services.clightning.extraConfig = '' services.clightning.extraConfig = ''
disable-dns disable-dns
disable-plugin=offers
''; '';
services.lnd = { services.lnd = {
@ -55,16 +59,19 @@ makeTestVM {
def parse_lndconnect_url(url): def parse_lndconnect_url(url):
u = Url.urlparse(url) u = Url.urlparse(url)
data = {'host': u.hostname, 'port': u.port}
queries = Url.parse_qs(u.query) queries = Url.parse_qs(u.query)
if url.startswith("clnrest"):
data['rune'] = queries['rune'][0]
else:
macaroon = queries['macaroon'][0] macaroon = queries['macaroon'][0]
is_clightning = url.startswith("c-lightning-rest") if url.startswith("c-lightning-rest"):
data['macaroon_hex'] = macaroon
else:
# lnd
data['macaroon_hex'] = base64.urlsafe_b64decode(macaroon + '===').hex().upper()
return SimpleNamespace( return SimpleNamespace(**data)
host = u.hostname,
port = u.port,
macaroon_hex =
macaroon if is_clightning else base64.urlsafe_b64decode(macaroon + '===').hex().upper()
)
client.start() client.start()
server.connect() server.connect()
@ -96,6 +103,16 @@ makeTestVM {
f"-X GET https://{api.host}:{api.port}/v1/getinfo" f"-X GET https://{api.host}:{api.port}/v1/getinfo"
) )
with subtest("lnconnect-clnrest-wg"):
server.wait_for_unit("clightning.service")
lndconnect_url = server.succeed("runuser -u operator -- lnconnect-clnrest-wg --url")
api = parse_lndconnect_url(lndconnect_url)
# Make clnrest API call
client.succeed(
f"curl -fsS --max-time 3 --insecure --header 'rune: {api.rune}' "
f"-X POST https://{api.host}:{api.port}/v1/getinfo"
)
with subtest("lndconnect-clightning-wg"): with subtest("lndconnect-clightning-wg"):
server.wait_for_unit("clightning-rest.service") server.wait_for_unit("clightning-rest.service")
lndconnect_url = server.succeed("runuser -u operator -- lndconnect-clightning-wg --url") lndconnect_url = server.succeed("runuser -u operator -- lndconnect-clightning-wg --url")