aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/nixos/modules/services/misc/rippled.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/misc/rippled.nix')
-rw-r--r--nixpkgs/nixos/modules/services/misc/rippled.nix432
1 files changed, 432 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/misc/rippled.nix b/nixpkgs/nixos/modules/services/misc/rippled.nix
new file mode 100644
index 00000000000..cdf61730de3
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/rippled.nix
@@ -0,0 +1,432 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.rippled;
+
+ b2i = val: if val then "1" else "0";
+
+ dbCfg = db: ''
+ type=${db.type}
+ path=${db.path}
+ ${optionalString (db.compression != null) ("compression=${b2i db.compression}") }
+ ${optionalString (db.onlineDelete != null) ("online_delete=${toString db.onlineDelete}")}
+ ${optionalString (db.advisoryDelete != null) ("advisory_delete=${b2i db.advisoryDelete}")}
+ ${db.extraOpts}
+ '';
+
+ rippledCfg = ''
+ [server]
+ ${concatMapStringsSep "\n" (n: "port_${n}") (attrNames cfg.ports)}
+
+ ${concatMapStrings (p: ''
+ [port_${p.name}]
+ ip=${p.ip}
+ port=${toString p.port}
+ protocol=${concatStringsSep "," p.protocol}
+ ${optionalString (p.user != "") "user=${p.user}"}
+ ${optionalString (p.password != "") "user=${p.password}"}
+ admin=${concatStringsSep "," p.admin}
+ ${optionalString (p.ssl.key != null) "ssl_key=${p.ssl.key}"}
+ ${optionalString (p.ssl.cert != null) "ssl_cert=${p.ssl.cert}"}
+ ${optionalString (p.ssl.chain != null) "ssl_chain=${p.ssl.chain}"}
+ '') (attrValues cfg.ports)}
+
+ [database_path]
+ ${cfg.databasePath}
+
+ [node_db]
+ ${dbCfg cfg.nodeDb}
+
+ ${optionalString (cfg.tempDb != null) ''
+ [temp_db]
+ ${dbCfg cfg.tempDb}''}
+
+ ${optionalString (cfg.importDb != null) ''
+ [import_db]
+ ${dbCfg cfg.importDb}''}
+
+ [ips]
+ ${concatStringsSep "\n" cfg.ips}
+
+ [ips_fixed]
+ ${concatStringsSep "\n" cfg.ipsFixed}
+
+ [validators]
+ ${concatStringsSep "\n" cfg.validators}
+
+ [node_size]
+ ${cfg.nodeSize}
+
+ [ledger_history]
+ ${toString cfg.ledgerHistory}
+
+ [fetch_depth]
+ ${toString cfg.fetchDepth}
+
+ [validation_quorum]
+ ${toString cfg.validationQuorum}
+
+ [sntp_servers]
+ ${concatStringsSep "\n" cfg.sntpServers}
+
+ ${optionalString cfg.statsd.enable ''
+ [insight]
+ server=statsd
+ address=${cfg.statsd.address}
+ prefix=${cfg.statsd.prefix}
+ ''}
+
+ [rpc_startup]
+ { "command": "log_level", "severity": "${cfg.logLevel}" }
+ '' + cfg.extraConfig;
+
+ portOptions = { name, ...}: {
+ options = {
+ name = mkOption {
+ internal = true;
+ default = name;
+ };
+
+ ip = mkOption {
+ default = "127.0.0.1";
+ description = "Ip where rippled listens.";
+ type = types.str;
+ };
+
+ port = mkOption {
+ description = "Port where rippled listens.";
+ type = types.int;
+ };
+
+ protocol = mkOption {
+ description = "Protocols expose by rippled.";
+ type = types.listOf (types.enum ["http" "https" "ws" "wss" "peer"]);
+ };
+
+ user = mkOption {
+ description = "When set, these credentials will be required on HTTP/S requests.";
+ type = types.str;
+ default = "";
+ };
+
+ password = mkOption {
+ description = "When set, these credentials will be required on HTTP/S requests.";
+ type = types.str;
+ default = "";
+ };
+
+ admin = mkOption {
+ description = "A comma-separated list of admin IP addresses.";
+ type = types.listOf types.str;
+ default = ["127.0.0.1"];
+ };
+
+ ssl = {
+ key = mkOption {
+ description = ''
+ Specifies the filename holding the SSL key in PEM format.
+ '';
+ default = null;
+ type = types.nullOr types.path;
+ };
+
+ cert = mkOption {
+ description = ''
+ Specifies the path to the SSL certificate file in PEM format.
+ This is not needed if the chain includes it.
+ '';
+ default = null;
+ type = types.nullOr types.path;
+ };
+
+ chain = mkOption {
+ description = ''
+ If you need a certificate chain, specify the path to the
+ certificate chain here. The chain may include the end certificate.
+ '';
+ default = null;
+ type = types.nullOr types.path;
+ };
+ };
+ };
+ };
+
+ dbOptions = {
+ options = {
+ type = mkOption {
+ description = "Rippled database type.";
+ type = types.enum ["rocksdb" "nudb"];
+ default = "rocksdb";
+ };
+
+ path = mkOption {
+ description = "Location to store the database.";
+ type = types.path;
+ default = cfg.databasePath;
+ };
+
+ compression = mkOption {
+ description = "Whether to enable snappy compression.";
+ type = types.nullOr types.bool;
+ default = null;
+ };
+
+ onlineDelete = mkOption {
+ description = "Enable automatic purging of older ledger information.";
+ type = types.nullOr (types.addCheck types.int (v: v > 256));
+ default = cfg.ledgerHistory;
+ };
+
+ advisoryDelete = mkOption {
+ description = ''
+ If set, then require administrative RPC call "can_delete"
+ to enable online deletion of ledger records.
+ '';
+ type = types.nullOr types.bool;
+ default = null;
+ };
+
+ extraOpts = mkOption {
+ description = "Extra database options.";
+ type = types.lines;
+ default = "";
+ };
+ };
+ };
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+ services.rippled = {
+ enable = mkEnableOption "rippled";
+
+ package = mkOption {
+ description = "Which rippled package to use.";
+ type = types.package;
+ default = pkgs.rippled;
+ defaultText = "pkgs.rippled";
+ };
+
+ ports = mkOption {
+ description = "Ports exposed by rippled";
+ type = with types; attrsOf (submodule portOptions);
+ default = {
+ rpc = {
+ port = 5005;
+ admin = ["127.0.0.1"];
+ protocol = ["http"];
+ };
+
+ peer = {
+ port = 51235;
+ ip = "0.0.0.0";
+ protocol = ["peer"];
+ };
+
+ ws_public = {
+ port = 5006;
+ ip = "0.0.0.0";
+ protocol = ["ws" "wss"];
+ };
+ };
+ };
+
+ nodeDb = mkOption {
+ description = "Rippled main database options.";
+ type = with types; nullOr (submodule dbOptions);
+ default = {
+ type = "rocksdb";
+ extraOpts = ''
+ open_files=2000
+ filter_bits=12
+ cache_mb=256
+ file_size_pb=8
+ file_size_mult=2;
+ '';
+ };
+ };
+
+ tempDb = mkOption {
+ description = "Rippled temporary database options.";
+ type = with types; nullOr (submodule dbOptions);
+ default = null;
+ };
+
+ importDb = mkOption {
+ description = "Settings for performing a one-time import.";
+ type = with types; nullOr (submodule dbOptions);
+ default = null;
+ };
+
+ nodeSize = mkOption {
+ description = ''
+ Rippled size of the node you are running.
+ "tiny", "small", "medium", "large", and "huge"
+ '';
+ type = types.enum ["tiny" "small" "medium" "large" "huge"];
+ default = "small";
+ };
+
+ ips = mkOption {
+ description = ''
+ List of hostnames or ips where the Ripple protocol is served.
+ For a starter list, you can either copy entries from:
+ https://ripple.com/ripple.txt or if you prefer you can let it
+ default to r.ripple.com 51235
+
+ A port may optionally be specified after adding a space to the
+ address. By convention, if known, IPs are listed in from most
+ to least trusted.
+ '';
+ type = types.listOf types.str;
+ default = ["r.ripple.com 51235"];
+ };
+
+ ipsFixed = mkOption {
+ description = ''
+ List of IP addresses or hostnames to which rippled should always
+ attempt to maintain peer connections with. This is useful for
+ manually forming private networks, for example to configure a
+ validation server that connects to the Ripple network through a
+ public-facing server, or for building a set of cluster peers.
+
+ A port may optionally be specified after adding a space to the address
+ '';
+ type = types.listOf types.str;
+ default = [];
+ };
+
+ validators = mkOption {
+ description = ''
+ List of nodes to always accept as validators. Nodes are specified by domain
+ or public key.
+ '';
+ type = types.listOf types.str;
+ default = [
+ "n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1"
+ "n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2"
+ "n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3"
+ "n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4"
+ "n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5"
+ ];
+ };
+
+ databasePath = mkOption {
+ description = ''
+ Path to the ripple database.
+ '';
+ type = types.path;
+ default = "/var/lib/rippled";
+ };
+
+ validationQuorum = mkOption {
+ description = ''
+ The minimum number of trusted validations a ledger must have before
+ the server considers it fully validated.
+ '';
+ type = types.int;
+ default = 3;
+ };
+
+ ledgerHistory = mkOption {
+ description = ''
+ The number of past ledgers to acquire on server startup and the minimum
+ to maintain while running.
+ '';
+ type = types.either types.int (types.enum ["full"]);
+ default = 1296000; # 1 month
+ };
+
+ fetchDepth = mkOption {
+ description = ''
+ The number of past ledgers to serve to other peers that request historical
+ ledger data (or "full" for no limit).
+ '';
+ type = types.either types.int (types.enum ["full"]);
+ default = "full";
+ };
+
+ sntpServers = mkOption {
+ description = ''
+ IP address or domain of NTP servers to use for time synchronization.;
+ '';
+ type = types.listOf types.str;
+ default = [
+ "time.windows.com"
+ "time.apple.com"
+ "time.nist.gov"
+ "pool.ntp.org"
+ ];
+ };
+
+ logLevel = mkOption {
+ description = "Logging verbosity.";
+ type = types.enum ["debug" "error" "info"];
+ default = "error";
+ };
+
+ statsd = {
+ enable = mkEnableOption "statsd monitoring for rippled";
+
+ address = mkOption {
+ description = "The UDP address and port of the listening StatsD server.";
+ default = "127.0.0.1:8125";
+ type = types.str;
+ };
+
+ prefix = mkOption {
+ description = "A string prepended to each collected metric.";
+ default = "";
+ type = types.str;
+ };
+ };
+
+ extraConfig = mkOption {
+ default = "";
+ description = ''
+ Extra lines to be added verbatim to the rippled.cfg configuration file.
+ '';
+ };
+
+ config = mkOption {
+ internal = true;
+ default = pkgs.writeText "rippled.conf" rippledCfg;
+ };
+ };
+ };
+
+
+ ###### implementation
+
+ config = mkIf cfg.enable {
+
+ users.users = singleton
+ { name = "rippled";
+ description = "Ripple server user";
+ uid = config.ids.uids.rippled;
+ home = cfg.databasePath;
+ createHome = true;
+ };
+
+ systemd.services.rippled = {
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/rippled --fg --conf ${cfg.config}";
+ User = "rippled";
+ Restart = "on-failure";
+ LimitNOFILE=10000;
+ };
+ };
+
+ environment.systemPackages = [ cfg.package ];
+
+ };
+}