diff --git a/modules/clightning-plugins/clnrest.nix b/modules/clightning-plugins/clnrest.nix new file mode 100644 index 0000000..a5a39ee --- /dev/null +++ b/modules/clightning-plugins/clnrest.nix @@ -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 + ''); + }; +} diff --git a/modules/clightning-plugins/default.nix b/modules/clightning-plugins/default.nix index 3142008..10744cf 100644 --- a/modules/clightning-plugins/default.nix +++ b/modules/clightning-plugins/default.nix @@ -13,6 +13,7 @@ let in { imports = [ ./clboss.nix + ./clnrest.nix ./feeadjuster.nix ./trustedcoin.nix ./zmq.nix diff --git a/modules/mempool.nix b/modules/mempool.nix index fc9af10..67382da 100644 --- a/modules/mempool.nix +++ b/modules/mempool.nix @@ -138,11 +138,7 @@ let # Internal read-only options used by `./nodeinfo.nix` and `./onion-services.nix` mempool-frontend = let - mkAlias = default: mkOption { - internal = true; - readOnly = true; - inherit default; - }; + inherit (nbLib) mkAlias; in { enable = mkAlias cfg.frontend.enable; address = mkAlias cfg.frontend.address; diff --git a/modules/netns-isolation.nix b/modules/netns-isolation.nix index 47b59dd..8d28343 100644 --- a/modules/netns-isolation.nix +++ b/modules/netns-isolation.nix @@ -315,6 +315,7 @@ in { }; services.clightning.address = netns.clightning.address; + services.clightning.plugins.clnrest.address = netns.clightning.address; services.lnd = { address = netns.lnd.address; diff --git a/modules/nodeinfo.nix b/modules/nodeinfo.nix index bce9ff0..3abeb57 100644 --- a/modules/nodeinfo.nix +++ b/modules/nodeinfo.nix @@ -142,6 +142,10 @@ in { '') + '' info["nodeid"] = shell("lncli getinfo | jq -r '.identity_pubkey'") '') name cfg; + clnrest = name: cfg: mkInfoLong { + inherit name cfg; + systemdServiceName = "clightning"; + }; clightning-rest = mkInfo ""; electrs = mkInfo ""; fulcrum = mkInfo ""; @@ -153,7 +157,6 @@ in { mempool-frontend = name: cfg: mkInfoLong { inherit name cfg; systemdServiceName = "nginx"; - extraCode = ""; }; # Only add sshd when it has an onion service sshd = name: cfg: mkIfOnionPort "sshd" (onionPort: '' diff --git a/pkgs/lib.nix b/pkgs/lib.nix index 522740e..5bfb639 100644 --- a/pkgs/lib.nix +++ b/pkgs/lib.nix @@ -123,4 +123,10 @@ let self = { mkIfTest = test: mkIf (config.tests.${test} or false); }; + mkAlias = default: mkOption { + internal = true; + readOnly = true; + inherit default; + }; + }; in self diff --git a/pkgs/python-packages/clnrest/default.nix b/pkgs/python-packages/clnrest/default.nix new file mode 100644 index 0000000..7f0e204 --- /dev/null +++ b/pkgs/python-packages/clnrest/default.nix @@ -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 diff --git a/pkgs/python-packages/default.nix b/pkgs/python-packages/default.nix index af056d5..7445beb 100644 --- a/pkgs/python-packages/default.nix +++ b/pkgs/python-packages/default.nix @@ -10,6 +10,7 @@ rec { pyln-proto = clightningPkg ./pyln-proto; pyln-bolt7 = clightningPkg ./pyln-bolt7; pylightning = clightningPkg ./pylightning; + clnrest = clightningPkg ./clnrest; # Packages only used by joinmarket bencoderpyx = callPackage ./bencoderpyx {}; diff --git a/test/tests.nix b/test/tests.nix index 6310cce..66aaf22 100644 --- a/test/tests.nix +++ b/test/tests.nix @@ -64,6 +64,7 @@ let nbPkgs = config.nix-bitcoin.pkgs; pluginPkgs = nbPkgs.clightning-plugins // { clboss.path = "${plugins.clboss.package}/bin/clboss"; + clnrest.path = "${plugins.clnrest.package}/bin/clnrest"; trustedcoin.path = "${plugins.trustedcoin.package}/bin/trustedcoin"; }; in map (plugin: pluginPkgs.${plugin}.path) enabled;