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