1{ config, lib, options, pkgs, ... }:
2
3let
4 cfg = config.services.wasabibackend;
5 opt = options.services.wasabibackend;
6
7 inherit (lib) literalExpression mkEnableOption mkIf mkOption optionalAttrs optionalString types;
8
9 confOptions = {
10 BitcoinRpcConnectionString = "${cfg.rpc.user}:${cfg.rpc.password}";
11 } // optionalAttrs (cfg.network == "mainnet") {
12 Network = "Main";
13 MainNetBitcoinP2pEndPoint = "${cfg.endpoint.ip}:${toString cfg.endpoint.port}";
14 MainNetBitcoinCoreRpcEndPoint = "${cfg.rpc.ip}:${toString cfg.rpc.port}";
15 } // optionalAttrs (cfg.network == "testnet") {
16 Network = "TestNet";
17 TestNetBitcoinP2pEndPoint = "${cfg.endpoint.ip}:${toString cfg.endpoint.port}";
18 TestNetBitcoinCoreRpcEndPoint = "${cfg.rpc.ip}:${toString cfg.rpc.port}";
19 } // optionalAttrs (cfg.network == "regtest") {
20 Network = "RegTest";
21 RegTestBitcoinP2pEndPoint = "${cfg.endpoint.ip}:${toString cfg.endpoint.port}";
22 RegTestBitcoinCoreRpcEndPoint = "${cfg.rpc.ip}:${toString cfg.rpc.port}";
23 };
24
25 configFile = pkgs.writeText "wasabibackend.conf" (builtins.toJSON confOptions);
26
27in {
28
29 options = {
30
31 services.wasabibackend = {
32 enable = mkEnableOption (lib.mdDoc "Wasabi backend service");
33
34 dataDir = mkOption {
35 type = types.path;
36 default = "/var/lib/wasabibackend";
37 description = lib.mdDoc "The data directory for the Wasabi backend node.";
38 };
39
40 customConfigFile = mkOption {
41 type = types.nullOr types.path;
42 default = null;
43 description = lib.mdDoc "Defines the path to a custom configuration file that is copied to the user's directory. Overrides any config options.";
44 };
45
46 network = mkOption {
47 type = types.enum [ "mainnet" "testnet" "regtest" ];
48 default = "mainnet";
49 description = lib.mdDoc "The network to use for the Wasabi backend service.";
50 };
51
52 endpoint = {
53 ip = mkOption {
54 type = types.str;
55 default = "127.0.0.1";
56 description = lib.mdDoc "IP address for P2P connection to bitcoind.";
57 };
58
59 port = mkOption {
60 type = types.port;
61 default = 8333;
62 description = lib.mdDoc "Port for P2P connection to bitcoind.";
63 };
64 };
65
66 rpc = {
67 ip = mkOption {
68 type = types.str;
69 default = "127.0.0.1";
70 description = lib.mdDoc "IP address for RPC connection to bitcoind.";
71 };
72
73 port = mkOption {
74 type = types.port;
75 default = 8332;
76 description = lib.mdDoc "Port for RPC connection to bitcoind.";
77 };
78
79 user = mkOption {
80 type = types.str;
81 default = "bitcoin";
82 description = lib.mdDoc "RPC user for the bitcoin endpoint.";
83 };
84
85 password = mkOption {
86 type = types.str;
87 default = "password";
88 description = lib.mdDoc "RPC password for the bitcoin endpoint. Warning: this is stored in cleartext in the Nix store! Use `configFile` or `passwordFile` if needed.";
89 };
90
91 passwordFile = mkOption {
92 type = types.nullOr types.path;
93 default = null;
94 description = lib.mdDoc "File that contains the password of the RPC user.";
95 };
96 };
97
98 user = mkOption {
99 type = types.str;
100 default = "wasabibackend";
101 description = lib.mdDoc "The user as which to run the wasabibackend node.";
102 };
103
104 group = mkOption {
105 type = types.str;
106 default = cfg.user;
107 defaultText = literalExpression "config.${opt.user}";
108 description = lib.mdDoc "The group as which to run the wasabibackend node.";
109 };
110 };
111 };
112
113 config = mkIf cfg.enable {
114
115 systemd.tmpfiles.rules = [
116 "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
117 ];
118
119 systemd.services.wasabibackend = {
120 description = "wasabibackend server";
121 wantedBy = [ "multi-user.target" ];
122 after = [ "network-online.target" ];
123 environment = {
124 DOTNET_PRINT_TELEMETRY_MESSAGE = "false";
125 DOTNET_CLI_TELEMETRY_OPTOUT = "true";
126 };
127 preStart = ''
128 mkdir -p ${cfg.dataDir}/.walletwasabi/backend
129 ${if cfg.customConfigFile != null then ''
130 cp -v ${cfg.customConfigFile} ${cfg.dataDir}/.walletwasabi/backend/Config.json
131 '' else ''
132 cp -v ${configFile} ${cfg.dataDir}/.walletwasabi/backend/Config.json
133 ${optionalString (cfg.rpc.passwordFile != null) ''
134 CONFIGTMP=$(mktemp)
135 cat ${cfg.dataDir}/.walletwasabi/backend/Config.json | ${pkgs.jq}/bin/jq --arg rpconnection "${cfg.rpc.user}:$(cat "${cfg.rpc.passwordFile}")" '. + { BitcoinRpcConnectionString: $rpconnection }' > $CONFIGTMP
136 mv $CONFIGTMP ${cfg.dataDir}/.walletwasabi/backend/Config.json
137 ''}
138 ''}
139 chmod ug+w ${cfg.dataDir}/.walletwasabi/backend/Config.json
140 '';
141 serviceConfig = {
142 User = cfg.user;
143 Group = cfg.group;
144 ExecStart = "${pkgs.wasabibackend}/bin/WasabiBackend";
145 ProtectSystem = "full";
146 };
147 };
148
149 users.users.${cfg.user} = {
150 name = cfg.user;
151 group = cfg.group;
152 description = "wasabibackend daemon user";
153 home = cfg.dataDir;
154 isSystemUser = true;
155 };
156
157 users.groups.${cfg.group} = {};
158
159 };
160}