at 15.09-beta 10 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 type = mkOption { 158 description = "Rippled database type."; 159 type = types.enum ["rocksdb" "nudb"]; 160 default = "rocksdb"; 161 }; 162 163 path = mkOption { 164 description = "Location to store the database."; 165 type = types.path; 166 default = cfg.databasePath; 167 }; 168 169 compression = mkOption { 170 description = "Whether to enable snappy compression."; 171 type = types.nullOr types.bool; 172 default = null; 173 }; 174 175 onlineDelete = mkOption { 176 description = "Enable automatic purging of older ledger information."; 177 type = types.addCheck (types.nullOr types.int) (v: v > 256); 178 default = cfg.ledgerHistory; 179 }; 180 181 advisoryDelete = mkOption { 182 description = '' 183 If set, then require administrative RPC call "can_delete" 184 to enable online deletion of ledger records. 185 ''; 186 type = types.nullOr types.bool; 187 default = null; 188 }; 189 190 extraOpts = mkOption { 191 description = "Extra database options."; 192 type = types.lines; 193 default = ""; 194 }; 195 }; 196 197in 198 199{ 200 201 ###### interface 202 203 options = { 204 services.rippled = { 205 enable = mkEnableOption "rippled"; 206 207 package = mkOption { 208 description = "Which rippled package to use."; 209 type = types.package; 210 default = pkgs.rippled; 211 }; 212 213 ports = mkOption { 214 description = "Ports exposed by rippled"; 215 type = types.attrsOf types.optionSet; 216 options = [portOptions]; 217 default = { 218 rpc = { 219 port = 5005; 220 admin = ["127.0.0.1"]; 221 protocol = ["http"]; 222 }; 223 224 peer = { 225 port = 51235; 226 ip = "0.0.0.0"; 227 protocol = ["peer"]; 228 }; 229 230 ws_public = { 231 port = 5006; 232 ip = "0.0.0.0"; 233 protocol = ["ws" "wss"]; 234 }; 235 }; 236 }; 237 238 nodeDb = mkOption { 239 description = "Rippled main database options."; 240 type = types.nullOr types.optionSet; 241 options = [dbOptions]; 242 default = { 243 type = "rocksdb"; 244 extraOpts = '' 245 open_files=2000 246 filter_bits=12 247 cache_mb=256 248 file_size_pb=8 249 file_size_mult=2; 250 ''; 251 }; 252 }; 253 254 tempDb = mkOption { 255 description = "Rippled temporary database options."; 256 type = types.nullOr types.optionSet; 257 options = [dbOptions]; 258 default = null; 259 }; 260 261 importDb = mkOption { 262 description = "Settings for performing a one-time import."; 263 type = types.nullOr types.optionSet; 264 options = [dbOptions]; 265 default = null; 266 }; 267 268 nodeSize = mkOption { 269 description = '' 270 Rippled size of the node you are running. 271 "tiny", "small", "medium", "large", and "huge" 272 ''; 273 type = types.enum ["tiny" "small" "medium" "large" "huge"]; 274 default = "small"; 275 }; 276 277 ips = mkOption { 278 description = '' 279 List of hostnames or ips where the Ripple protocol is served. 280 For a starter list, you can either copy entries from: 281 https://ripple.com/ripple.txt or if you prefer you can let it 282 default to r.ripple.com 51235 283 284 A port may optionally be specified after adding a space to the 285 address. By convention, if known, IPs are listed in from most 286 to least trusted. 287 ''; 288 type = types.listOf types.str; 289 default = ["r.ripple.com 51235"]; 290 }; 291 292 ipsFixed = mkOption { 293 description = '' 294 List of IP addresses or hostnames to which rippled should always 295 attempt to maintain peer connections with. This is useful for 296 manually forming private networks, for example to configure a 297 validation server that connects to the Ripple network through a 298 public-facing server, or for building a set of cluster peers. 299 300 A port may optionally be specified after adding a space to the address 301 ''; 302 type = types.listOf types.str; 303 default = []; 304 }; 305 306 validators = mkOption { 307 description = '' 308 List of nodes to always accept as validators. Nodes are specified by domain 309 or public key. 310 ''; 311 type = types.listOf types.str; 312 default = [ 313 "n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1" 314 "n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2" 315 "n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3" 316 "n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4" 317 "n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5" 318 ]; 319 }; 320 321 databasePath = mkOption { 322 description = '' 323 Path to the ripple database. 324 ''; 325 type = types.path; 326 default = "/var/lib/rippled"; 327 }; 328 329 validationQuorum = mkOption { 330 description = '' 331 The minimum number of trusted validations a ledger must have before 332 the server considers it fully validated. 333 ''; 334 type = types.int; 335 default = 3; 336 }; 337 338 ledgerHistory = mkOption { 339 description = '' 340 The number of past ledgers to acquire on server startup and the minimum 341 to maintain while running. 342 ''; 343 type = types.either types.int (types.enum ["full"]); 344 default = 1296000; # 1 month 345 }; 346 347 fetchDepth = mkOption { 348 description = '' 349 The number of past ledgers to serve to other peers that request historical 350 ledger data (or "full" for no limit). 351 ''; 352 type = types.either types.int (types.enum ["full"]); 353 default = "full"; 354 }; 355 356 sntpServers = mkOption { 357 description = '' 358 IP address or domain of NTP servers to use for time synchronization.; 359 ''; 360 type = types.listOf types.str; 361 default = [ 362 "time.windows.com" 363 "time.apple.com" 364 "time.nist.gov" 365 "pool.ntp.org" 366 ]; 367 }; 368 369 logLevel = mkOption { 370 description = "Logging verbosity."; 371 type = types.enum ["debug" "error" "info"]; 372 default = "error"; 373 }; 374 375 statsd = { 376 enable = mkEnableOption "statsd monitoring for rippled"; 377 378 address = mkOption { 379 description = "The UDP address and port of the listening StatsD server."; 380 default = "127.0.0.1:8125"; 381 type = types.str; 382 }; 383 384 prefix = mkOption { 385 description = "A string prepended to each collected metric."; 386 default = ""; 387 type = types.str; 388 }; 389 }; 390 391 extraConfig = mkOption { 392 default = ""; 393 description = '' 394 Extra lines to be added verbatim to the rippled.cfg configuration file. 395 ''; 396 }; 397 398 config = mkOption { 399 internal = true; 400 default = pkgs.writeText "rippled.conf" rippledCfg; 401 }; 402 }; 403 }; 404 405 406 ###### implementation 407 408 config = mkIf cfg.enable { 409 410 users.extraUsers = singleton 411 { name = "rippled"; 412 description = "Ripple server user"; 413 uid = config.ids.uids.rippled; 414 home = cfg.databasePath; 415 createHome = true; 416 }; 417 418 systemd.services.rippled = { 419 after = [ "network.target" ]; 420 wantedBy = [ "multi-user.target" ]; 421 422 serviceConfig = { 423 ExecStart = "${cfg.package}/bin/rippled --fg --conf ${cfg.config}"; 424 User = "rippled"; 425 Restart = "on-failure"; 426 LimitNOFILE=10000; 427 }; 428 }; 429 430 environment.systemPackages = [ cfg.package ]; 431 432 }; 433}