1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.monero;
7 dataDir = "/var/lib/monero";
8
9 listToConf = option: list:
10 concatMapStrings (value: "${option}=${value}\n") list;
11
12 login = (cfg.rpc.user != null && cfg.rpc.password != null);
13
14 configFile = with cfg; pkgs.writeText "monero.conf" ''
15 log-file=/dev/stdout
16 data-dir=${dataDir}
17
18 ${optionalString mining.enable ''
19 start-mining=${mining.address}
20 mining-threads=${toString mining.threads}
21 ''}
22
23 rpc-bind-ip=${rpc.address}
24 rpc-bind-port=${toString rpc.port}
25 ${optionalString login ''
26 rpc-login=${rpc.user}:${rpc.password}
27 ''}
28 ${optionalString rpc.restricted ''
29 restrict-rpc=1
30 ''}
31
32 limit-rate-up=${toString limits.upload}
33 limit-rate-down=${toString limits.download}
34 max-concurrency=${toString limits.threads}
35 block-sync-size=${toString limits.syncSize}
36
37 ${listToConf "add-peer" extraNodes}
38 ${listToConf "add-priority-node" priorityNodes}
39 ${listToConf "add-exclusive-node" exclusiveNodes}
40
41 ${extraConfig}
42 '';
43
44in
45
46{
47
48 ###### interface
49
50 options = {
51
52 services.monero = {
53
54 enable = mkEnableOption "Monero node daemon.";
55
56 mining.enable = mkOption {
57 type = types.bool;
58 default = false;
59 description = ''
60 Whether to mine moneroj.
61 '';
62 };
63
64 mining.address = mkOption {
65 type = types.str;
66 default = "";
67 description = ''
68 Monero address where to send mining rewards.
69 '';
70 };
71
72 mining.threads = mkOption {
73 type = types.addCheck types.int (x: x>=0);
74 default = 0;
75 description = ''
76 Number of threads used for mining.
77 Set to <literal>0</literal> to use all available.
78 '';
79 };
80
81 rpc.user = mkOption {
82 type = types.nullOr types.str;
83 default = null;
84 description = ''
85 User name for RPC connections.
86 '';
87 };
88
89 rpc.password = mkOption {
90 type = types.str;
91 default = null;
92 description = ''
93 Password for RPC connections.
94 '';
95 };
96
97 rpc.address = mkOption {
98 type = types.str;
99 default = "127.0.0.1";
100 description = ''
101 IP address the RPC server will bind to.
102 '';
103 };
104
105 rpc.port = mkOption {
106 type = types.int;
107 default = 18081;
108 description = ''
109 Port the RPC server will bind to.
110 '';
111 };
112
113 rpc.restricted = mkOption {
114 type = types.bool;
115 default = false;
116 description = ''
117 Whether to restrict RPC to view only commands.
118 '';
119 };
120
121 limits.upload = mkOption {
122 type = types.addCheck types.int (x: x>=-1);
123 default = -1;
124 description = ''
125 Limit of the upload rate in kB/s.
126 Set to <literal>-1</literal> to leave unlimited.
127 '';
128 };
129
130 limits.download = mkOption {
131 type = types.addCheck types.int (x: x>=-1);
132 default = -1;
133 description = ''
134 Limit of the download rate in kB/s.
135 Set to <literal>-1</literal> to leave unlimited.
136 '';
137 };
138
139 limits.threads = mkOption {
140 type = types.addCheck types.int (x: x>=0);
141 default = 0;
142 description = ''
143 Maximum number of threads used for a parallel job.
144 Set to <literal>0</literal> to leave unlimited.
145 '';
146 };
147
148 limits.syncSize = mkOption {
149 type = types.addCheck types.int (x: x>=0);
150 default = 0;
151 description = ''
152 Maximum number of blocks to sync at once.
153 Set to <literal>0</literal> for adaptive.
154 '';
155 };
156
157 extraNodes = mkOption {
158 type = types.listOf types.str;
159 default = [ ];
160 description = ''
161 List of additional peer IP addresses to add to the local list.
162 '';
163 };
164
165 priorityNodes = mkOption {
166 type = types.listOf types.str;
167 default = [ ];
168 description = ''
169 List of peer IP addresses to connect to and
170 attempt to keep the connection open.
171 '';
172 };
173
174 exclusiveNodes = mkOption {
175 type = types.listOf types.str;
176 default = [ ];
177 description = ''
178 List of peer IP addresses to connect to *only*.
179 If given the other peer options will be ignored.
180 '';
181 };
182
183 extraConfig = mkOption {
184 type = types.lines;
185 default = "";
186 description = ''
187 Extra lines to be added verbatim to monerod configuration.
188 '';
189 };
190
191 };
192
193 };
194
195
196 ###### implementation
197
198 config = mkIf cfg.enable {
199
200 users.extraUsers = singleton {
201 name = "monero";
202 uid = config.ids.uids.monero;
203 description = "Monero daemon user";
204 home = dataDir;
205 createHome = true;
206 };
207
208 users.extraGroups = singleton {
209 name = "monero";
210 gid = config.ids.gids.monero;
211 };
212
213 systemd.services.monero = {
214 description = "monero daemon";
215 after = [ "network.target" ];
216 wantedBy = [ "multi-user.target" ];
217
218 serviceConfig = {
219 User = "monero";
220 Group = "monero";
221 ExecStart = "${pkgs.monero}/bin/monerod --config-file=${configFile} --non-interactive";
222 Restart = "always";
223 SuccessExitStatus = [ 0 1 ];
224 };
225 };
226
227 assertions = singleton {
228 assertion = cfg.mining.enable -> cfg.mining.address != "";
229 message = ''
230 You need a Monero address to receive mining rewards:
231 specify one using option monero.mining.address.
232 '';
233 };
234
235 };
236
237}
238