python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / misc / rippled.nix
blobd14b6421b7424b0796805344c38154441748af05
1 { config, lib, options, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.rippled;
7   opt = options.services.rippled;
9   b2i = val: if val then "1" else "0";
11   dbCfg = db: ''
12     type=${db.type}
13     path=${db.path}
14     ${optionalString (db.compression != null) ("compression=${b2i db.compression}") }
15     ${optionalString (db.onlineDelete != null) ("online_delete=${toString db.onlineDelete}")}
16     ${optionalString (db.advisoryDelete != null) ("advisory_delete=${b2i db.advisoryDelete}")}
17     ${db.extraOpts}
18   '';
20   rippledCfg = ''
21     [server]
22     ${concatMapStringsSep "\n" (n: "port_${n}") (attrNames cfg.ports)}
24     ${concatMapStrings (p: ''
25     [port_${p.name}]
26     ip=${p.ip}
27     port=${toString p.port}
28     protocol=${concatStringsSep "," p.protocol}
29     ${optionalString (p.user != "") "user=${p.user}"}
30     ${optionalString (p.password != "") "user=${p.password}"}
31     admin=${concatStringsSep "," p.admin}
32     ${optionalString (p.ssl.key != null) "ssl_key=${p.ssl.key}"}
33     ${optionalString (p.ssl.cert != null) "ssl_cert=${p.ssl.cert}"}
34     ${optionalString (p.ssl.chain != null) "ssl_chain=${p.ssl.chain}"}
35     '') (attrValues cfg.ports)}
37     [database_path]
38     ${cfg.databasePath}
40     [node_db]
41     ${dbCfg cfg.nodeDb}
43     ${optionalString (cfg.tempDb != null) ''
44     [temp_db]
45     ${dbCfg cfg.tempDb}''}
47     ${optionalString (cfg.importDb != null) ''
48     [import_db]
49     ${dbCfg cfg.importDb}''}
51     [ips]
52     ${concatStringsSep "\n" cfg.ips}
54     [ips_fixed]
55     ${concatStringsSep "\n" cfg.ipsFixed}
57     [validators]
58     ${concatStringsSep "\n" cfg.validators}
60     [node_size]
61     ${cfg.nodeSize}
63     [ledger_history]
64     ${toString cfg.ledgerHistory}
66     [fetch_depth]
67     ${toString cfg.fetchDepth}
69     [validation_quorum]
70     ${toString cfg.validationQuorum}
72     [sntp_servers]
73     ${concatStringsSep "\n" cfg.sntpServers}
75     ${optionalString cfg.statsd.enable ''
76     [insight]
77     server=statsd
78     address=${cfg.statsd.address}
79     prefix=${cfg.statsd.prefix}
80     ''}
82     [rpc_startup]
83     { "command": "log_level", "severity": "${cfg.logLevel}" }
84   '' + cfg.extraConfig;
86   portOptions = { name, ...}: {
87     options = {
88       name = mkOption {
89         internal = true;
90         default = name;
91       };
93       ip = mkOption {
94         default = "127.0.0.1";
95         description = lib.mdDoc "Ip where rippled listens.";
96         type = types.str;
97       };
99       port = mkOption {
100         description = lib.mdDoc "Port where rippled listens.";
101         type = types.port;
102       };
104       protocol = mkOption {
105         description = lib.mdDoc "Protocols expose by rippled.";
106         type = types.listOf (types.enum ["http" "https" "ws" "wss" "peer"]);
107       };
109       user = mkOption {
110         description = lib.mdDoc "When set, these credentials will be required on HTTP/S requests.";
111         type = types.str;
112         default = "";
113       };
115       password = mkOption {
116         description = lib.mdDoc "When set, these credentials will be required on HTTP/S requests.";
117         type = types.str;
118         default = "";
119       };
121       admin = mkOption {
122         description = lib.mdDoc "A comma-separated list of admin IP addresses.";
123         type = types.listOf types.str;
124         default = ["127.0.0.1"];
125       };
127       ssl = {
128         key = mkOption {
129           description = lib.mdDoc ''
130             Specifies the filename holding the SSL key in PEM format.
131           '';
132           default = null;
133           type = types.nullOr types.path;
134         };
136         cert = mkOption {
137           description = lib.mdDoc ''
138             Specifies the path to the SSL certificate file in PEM format.
139             This is not needed if the chain includes it.
140           '';
141           default = null;
142           type = types.nullOr types.path;
143         };
145         chain = mkOption {
146           description = lib.mdDoc ''
147             If you need a certificate chain, specify the path to the
148             certificate chain here. The chain may include the end certificate.
149           '';
150           default = null;
151           type = types.nullOr types.path;
152         };
153       };
154     };
155   };
157   dbOptions = {
158     options = {
159       type = mkOption {
160         description = lib.mdDoc "Rippled database type.";
161         type = types.enum ["rocksdb" "nudb"];
162         default = "rocksdb";
163       };
165       path = mkOption {
166         description = lib.mdDoc "Location to store the database.";
167         type = types.path;
168         default = cfg.databasePath;
169         defaultText = literalExpression "config.${opt.databasePath}";
170       };
172       compression = mkOption {
173         description = lib.mdDoc "Whether to enable snappy compression.";
174         type = types.nullOr types.bool;
175         default = null;
176       };
178       onlineDelete = mkOption {
179         description = lib.mdDoc "Enable automatic purging of older ledger information.";
180         type = types.nullOr (types.addCheck types.int (v: v > 256));
181         default = cfg.ledgerHistory;
182         defaultText = literalExpression "config.${opt.ledgerHistory}";
183       };
185       advisoryDelete = mkOption {
186         description = lib.mdDoc ''
187           If set, then require administrative RPC call "can_delete"
188           to enable online deletion of ledger records.
189         '';
190         type = types.nullOr types.bool;
191         default = null;
192       };
194       extraOpts = mkOption {
195         description = lib.mdDoc "Extra database options.";
196         type = types.lines;
197         default = "";
198       };
199     };
200   };
206   ###### interface
208   options = {
209     services.rippled = {
210       enable = mkEnableOption (lib.mdDoc "rippled");
212       package = mkOption {
213         description = lib.mdDoc "Which rippled package to use.";
214         type = types.package;
215         default = pkgs.rippled;
216         defaultText = literalExpression "pkgs.rippled";
217       };
219       ports = mkOption {
220         description = lib.mdDoc "Ports exposed by rippled";
221         type = with types; attrsOf (submodule portOptions);
222         default = {
223           rpc = {
224             port = 5005;
225             admin = ["127.0.0.1"];
226             protocol = ["http"];
227           };
229           peer = {
230             port = 51235;
231             ip = "0.0.0.0";
232             protocol = ["peer"];
233           };
235           ws_public = {
236             port = 5006;
237             ip = "0.0.0.0";
238             protocol = ["ws" "wss"];
239           };
240         };
241       };
243       nodeDb = mkOption {
244         description = lib.mdDoc "Rippled main database options.";
245         type = with types; nullOr (submodule dbOptions);
246         default = {
247           type = "rocksdb";
248           extraOpts = ''
249             open_files=2000
250             filter_bits=12
251             cache_mb=256
252             file_size_pb=8
253             file_size_mult=2;
254           '';
255         };
256       };
258       tempDb = mkOption {
259         description = lib.mdDoc "Rippled temporary database options.";
260         type = with types; nullOr (submodule dbOptions);
261         default = null;
262       };
264       importDb = mkOption {
265         description = lib.mdDoc "Settings for performing a one-time import.";
266         type = with types; nullOr (submodule dbOptions);
267         default = null;
268       };
270       nodeSize = mkOption {
271         description = lib.mdDoc ''
272           Rippled size of the node you are running.
273           "tiny", "small", "medium", "large", and "huge"
274         '';
275         type = types.enum ["tiny" "small" "medium" "large" "huge"];
276         default = "small";
277       };
279       ips = mkOption {
280         description = lib.mdDoc ''
281           List of hostnames or ips where the Ripple protocol is served.
282           For a starter list, you can either copy entries from:
283           https://ripple.com/ripple.txt or if you prefer you can let it
284            default to r.ripple.com 51235
286           A port may optionally be specified after adding a space to the
287           address. By convention, if known, IPs are listed in from most
288           to least trusted.
289         '';
290         type = types.listOf types.str;
291         default = ["r.ripple.com 51235"];
292       };
294       ipsFixed = mkOption {
295         description = lib.mdDoc ''
296           List of IP addresses or hostnames to which rippled should always
297           attempt to maintain peer connections with. This is useful for
298           manually forming private networks, for example to configure a
299           validation server that connects to the Ripple network through a
300           public-facing server, or for building a set of cluster peers.
302           A port may optionally be specified after adding a space to the address
303         '';
304         type = types.listOf types.str;
305         default = [];
306       };
308       validators = mkOption {
309         description = lib.mdDoc ''
310           List of nodes to always accept as validators. Nodes are specified by domain
311           or public key.
312         '';
313         type = types.listOf types.str;
314         default = [
315           "n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7  RL1"
316           "n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj  RL2"
317           "n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C  RL3"
318           "n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS  RL4"
319           "n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA  RL5"
320         ];
321       };
323       databasePath = mkOption {
324         description = lib.mdDoc ''
325           Path to the ripple database.
326         '';
327         type = types.path;
328         default = "/var/lib/rippled";
329       };
331       validationQuorum = mkOption {
332         description = lib.mdDoc ''
333           The minimum number of trusted validations a ledger must have before
334           the server considers it fully validated.
335         '';
336         type = types.int;
337         default = 3;
338       };
340       ledgerHistory = mkOption {
341         description = lib.mdDoc ''
342           The number of past ledgers to acquire on server startup and the minimum
343           to maintain while running.
344         '';
345         type = types.either types.int (types.enum ["full"]);
346         default = 1296000; # 1 month
347       };
349       fetchDepth = mkOption {
350         description = lib.mdDoc ''
351           The number of past ledgers to serve to other peers that request historical
352           ledger data (or "full" for no limit).
353         '';
354         type = types.either types.int (types.enum ["full"]);
355         default = "full";
356       };
358       sntpServers = mkOption {
359         description = lib.mdDoc ''
360           IP address or domain of NTP servers to use for time synchronization.;
361         '';
362         type = types.listOf types.str;
363         default = [
364           "time.windows.com"
365           "time.apple.com"
366           "time.nist.gov"
367           "pool.ntp.org"
368         ];
369       };
371       logLevel = mkOption {
372         description = lib.mdDoc "Logging verbosity.";
373         type = types.enum ["debug" "error" "info"];
374         default = "error";
375       };
377       statsd = {
378         enable = mkEnableOption (lib.mdDoc "statsd monitoring for rippled");
380         address = mkOption {
381           description = lib.mdDoc "The UDP address and port of the listening StatsD server.";
382           default = "127.0.0.1:8125";
383           type = types.str;
384         };
386         prefix = mkOption {
387           description = lib.mdDoc "A string prepended to each collected metric.";
388           default = "";
389           type = types.str;
390         };
391       };
393       extraConfig = mkOption {
394         default = "";
395         type = types.lines;
396         description = lib.mdDoc ''
397           Extra lines to be added verbatim to the rippled.cfg configuration file.
398         '';
399       };
401       config = mkOption {
402         internal = true;
403         default = pkgs.writeText "rippled.conf" rippledCfg;
404         defaultText = literalMD "generated config file";
405       };
406     };
407   };
410   ###### implementation
412   config = mkIf cfg.enable {
414     users.users.rippled = {
415         description = "Ripple server user";
416         isSystemUser = true;
417         group = "rippled";
418         home = cfg.databasePath;
419         createHome = true;
420       };
421     users.groups.rippled = {};
423     systemd.services.rippled = {
424       after = [ "network.target" ];
425       wantedBy = [ "multi-user.target" ];
427       serviceConfig = {
428         ExecStart = "${cfg.package}/bin/rippled --fg --conf ${cfg.config}";
429         User = "rippled";
430         Restart = "on-failure";
431         LimitNOFILE=10000;
432       };
433     };
435     environment.systemPackages = [ cfg.package ];
437   };