diff --git a/modules/joinmarket.nix b/modules/joinmarket.nix index 34343fa83..3a31f20e3 100644 --- a/modules/joinmarket.nix +++ b/modules/joinmarket.nix @@ -131,6 +131,62 @@ let ''; }; }; + + jmwalletd = { + enable = mkEnableOption "JoinMarket jmwalletd"; + port = mkOption { + type = types.port; + default = 28183; + description = mdDoc "The port over which to serve RPC."; + }; + wssPort = mkOption { + type = types.port; + default = 28283; + description = mdDoc "The port over which to serve websocket subscriptions"; + }; + extraArgs = mkOption { + type = types.separatedString " "; + default = ""; + description = mdDoc "Extra coomand line arguments passed to jmwalletd"; + }; + dataDir = mkOption { + readOnly = true; + type = types.path; + default = cfg.dataDir; + description = mdDoc "The data directory for JoinMarket's jmwalled."; + }; + certPath = mkOption { + readOnly = true; + default = "${secretsDir}/jm-jmwalletd"; + description = mdDoc "JoinMarket jmwalletd TLS certificate path."; + }; + recoverSync = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Choose to do detailed wallet sync, used for recovering on new Core + instance. + ''; + }; + certificate = { + extraIPs = mkOption { + type = with types; listOf str; + default = []; + example = [ "60.100.0.1" ]; + description = mdDoc '' + Extra `subjectAltName` IPs added to the certificate. + ''; + }; + extraDomains = mkOption { + type = with types; listOf str; + default = []; + example = [ "example.com" ]; + description = mdDoc '' + Extra `subjectAltName` domain names added to the certificate. + ''; + }; + }; + }; }; cfg = config.services.joinmarket; @@ -388,6 +444,45 @@ in { ''; } + (mkIf cfg.jmwalletd.enable { + nix-bitcoin.secrets.jm-jmwalletd-cert.user = cfg.user; + nix-bitcoin.secrets.jm-jmwalletd-key.user = cfg.user; + nix-bitcoin.generateSecretsCmds.joinmarket = '' + makeCert jm-jmwalletd '${nbLib.mkCertExtraAltNames cfg.jmwalletd.certificate}' + ''; + + systemd.services.joinmarket-jmwalletd = let + jmwalletd_ssl_dir = "${cfg.jmwalletd.dataDir}/ssl"; + in{ + wantedBy = [ "joinmarket.service" ]; + requires = [ "joinmarket.service" ]; + after = [ "joinmarket.service" "nix-bitcoin-secrets.target" ]; + preStart = '' + # Copy the certificates into a data directory under the `ssl` dir + mkdir -p '${jmwalletd_ssl_dir}' + install -m600 '${cfg.jmwalletd.certPath}-cert' '${jmwalletd_ssl_dir}/cert.pem' + install -m600 '${cfg.jmwalletd.certPath}-key' '${jmwalletd_ssl_dir}/key.pem' + ''; + serviceConfig = nbLib.defaultHardening // { + WorkingDirectory = cfg.dataDir; + User = cfg.user; + ExecStart = '' + ${config.nix-bitcoin.pkgs.joinmarket}/bin/jm-jmwalletd \ + --port='${toString cfg.jmwalletd.port}' \ + --wss-port='${toString cfg.jmwalletd.wssPort}' \ + --datadir='${cfg.jmwalletd.dataDir}' \ + ${optionalString (cfg.jmwalletd.recoverSync) "--recoversync \\"} + ${cfg.jmwalletd.extraArgs} + ''; + SyslogIdentifier = "joinmarket-jmwalletd"; + ReadWritePaths = [ cfg.dataDir ]; + Restart = "on-failure"; + RestartSec = "10s"; + MemoryDenyWriteExecute = false; + } // nbLib.allowTor; + }; + }) + (mkIf cfg.yieldgenerator.enable { systemd.services.joinmarket-yieldgenerator = { wantedBy = [ "joinmarket.service" ]; diff --git a/modules/netns-isolation.nix b/modules/netns-isolation.nix index 832bb9444..18fe9106c 100644 --- a/modules/netns-isolation.nix +++ b/modules/netns-isolation.nix @@ -345,8 +345,14 @@ in { messagingAddress = netns.joinmarket.address; cliExec = mkCliExec "joinmarket"; }; - systemd.services.joinmarket-yieldgenerator = mkIf config.services.joinmarket.yieldgenerator.enable { - serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket"; + systemd.services = { + joinmarket-yieldgenerator = mkIf config.services.joinmarket.yieldgenerator.enable { + serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket"; + }; + + joinmarket-jmwalletd = mkIf config.services.joinmarket.jmwalletd.enable { + serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket"; + }; }; services.joinmarket-ob-watcher.address = netns.joinmarket-ob-watcher.address; diff --git a/modules/nodeinfo.nix b/modules/nodeinfo.nix index f219664ff..85b82cc5d 100644 --- a/modules/nodeinfo.nix +++ b/modules/nodeinfo.nix @@ -148,6 +148,8 @@ in { btcpayserver = mkInfo ""; liquidd = mkInfo ""; joinmarket-ob-watcher = mkInfo ""; + # FIXME: add info for jmwalletd? + #joinmarket-jmwalletd = mkInfo ""; rtl = mkInfo ""; mempool = mkInfo ""; mempool-frontend = name: cfg: mkInfoLong { diff --git a/test/tests.nix b/test/tests.nix index 1d39d29d5..17955680e 100644 --- a/test/tests.nix +++ b/test/tests.nix @@ -122,14 +122,20 @@ let tests.joinmarket = cfg.joinmarket.enable; tests.joinmarket-yieldgenerator = cfg.joinmarket.yieldgenerator.enable; + tests.joinmarket-jmwalletd = cfg.joinmarket.jmwalletd.enable; tests.joinmarket-ob-watcher = cfg.joinmarket-ob-watcher.enable; - services.joinmarket.yieldgenerator = { - enable = config.services.joinmarket.enable; - # Test a smattering of custom parameters - ordertype = "absoffer"; - cjfee_a = 300; - cjfee_r = 0.00003; - txfee = 200; + services.joinmarket = { + yieldgenerator = { + enable = config.services.joinmarket.enable; + # Test a smattering of custom parameters + ordertype = "absoffer"; + cjfee_a = 300; + cjfee_r = 0.00003; + txfee = 200; + }; + jmwalletd = { + enable = config.services.joinmarket.enable; + }; }; tests.nodeinfo = config.nix-bitcoin.nodeinfo.enable; diff --git a/test/tests.py b/test/tests.py index ef38b9de5..e3eaea945 100644 --- a/test/tests.py +++ b/test/tests.py @@ -281,6 +281,14 @@ def _(): assert_running("joinmarket-ob-watcher") machine.wait_until_succeeds(log_has_string("joinmarket-ob-watcher", "Starting ob-watcher")) +@test("joinmarket-jmwalletd") +def _(): + assert_running("joinmarket-jmwalletd") + machine.wait_until_succeeds(log_has_string("joinmarket-jmwalletd", "Started joinmarket-jmwalletd.service.")) + machine.wait_until_succeeds(log_has_string("joinmarket-jmwalletd", "Starting jmwalletd on port: 28183")) + wait_for_open_port(ip("joinmarket"), 28183) # RPC port + wait_for_open_port(ip("joinmarket"), 28283) # Websocket port + @test("nodeinfo") def _(): status, _ = machine.execute("systemctl is-enabled --quiet onion-addresses 2> /dev/null")