1 { config, lib, pkgs, ... }:
3 eachGeth = config.services.geth;
5 gethOpts = { config, lib, name, ...}: {
9 enable = lib.mkEnableOption "Go Ethereum Node";
12 type = lib.types.port;
14 description = "Port number Go Ethereum will be listening on, both TCP and UDP.";
18 enable = lib.mkEnableOption "Go Ethereum HTTP API";
19 address = lib.mkOption {
21 default = "127.0.0.1";
22 description = "Listen address of Go Ethereum HTTP API.";
26 type = lib.types.port;
28 description = "Port number of Go Ethereum HTTP API.";
32 type = lib.types.nullOr (lib.types.listOf lib.types.str);
34 description = "APIs to enable over WebSocket";
35 example = ["net" "eth"];
40 enable = lib.mkEnableOption "Go Ethereum WebSocket API";
41 address = lib.mkOption {
43 default = "127.0.0.1";
44 description = "Listen address of Go Ethereum WebSocket API.";
48 type = lib.types.port;
50 description = "Port number of Go Ethereum WebSocket API.";
54 type = lib.types.nullOr (lib.types.listOf lib.types.str);
56 description = "APIs to enable over WebSocket";
57 example = ["net" "eth"];
62 enable = lib.mkEnableOption "Go Ethereum Auth RPC API";
63 address = lib.mkOption {
65 default = "127.0.0.1";
66 description = "Listen address of Go Ethereum Auth RPC API.";
70 type = lib.types.port;
72 description = "Port number of Go Ethereum Auth RPC API.";
75 vhosts = lib.mkOption {
76 type = lib.types.nullOr (lib.types.listOf lib.types.str);
77 default = ["localhost"];
78 description = "List of virtual hostnames from which to accept requests.";
79 example = ["localhost" "geth.example.org"];
82 jwtsecret = lib.mkOption {
85 description = "Path to a JWT secret for authenticated RPC endpoint.";
86 example = "/var/run/geth/jwtsecret";
91 enable = lib.mkEnableOption "Go Ethereum prometheus metrics";
92 address = lib.mkOption {
94 default = "127.0.0.1";
95 description = "Listen address of Go Ethereum metrics service.";
99 type = lib.types.port;
101 description = "Port number of Go Ethereum metrics service.";
105 network = lib.mkOption {
106 type = lib.types.nullOr (lib.types.enum [ "goerli" "rinkeby" "yolov2" "ropsten" ]);
108 description = "The network to connect to. Mainnet (null) is the default ethereum network.";
111 syncmode = lib.mkOption {
112 type = lib.types.enum [ "snap" "fast" "full" "light" ];
114 description = "Blockchain sync mode.";
117 gcmode = lib.mkOption {
118 type = lib.types.enum [ "full" "archive" ];
120 description = "Blockchain garbage collection mode.";
123 maxpeers = lib.mkOption {
124 type = lib.types.int;
126 description = "Maximum peers to connect to.";
129 extraArgs = lib.mkOption {
130 type = lib.types.listOf lib.types.str;
131 description = "Additional arguments passed to Go Ethereum.";
135 package = lib.mkPackageOption pkgs [ "go-ethereum" "geth" ] { };
145 services.geth = lib.mkOption {
146 type = lib.types.attrsOf (lib.types.submodule gethOpts);
148 description = "Specification of one or more geth instances.";
152 ###### implementation
154 config = lib.mkIf (eachGeth != {}) {
156 environment.systemPackages = lib.flatten (lib.mapAttrsToList (gethName: cfg: [
160 systemd.services = lib.mapAttrs' (gethName: cfg: let
161 stateDir = "goethereum/${gethName}/${if (cfg.network == null) then "mainnet" else cfg.network}";
162 dataDir = "/var/lib/${stateDir}";
164 lib.nameValuePair "geth-${gethName}" (lib.mkIf cfg.enable {
165 description = "Go Ethereum node (${gethName})";
166 wantedBy = [ "multi-user.target" ];
167 after = [ "network.target" ];
172 StateDirectory = stateDir;
176 ProtectSystem = "full";
177 NoNewPrivileges = "true";
178 PrivateDevices = "true";
179 MemoryDenyWriteExecute = "true";
183 ${cfg.package}/bin/geth \
186 ${lib.optionalString (cfg.network != null) ''--${cfg.network}''} \
187 --syncmode ${cfg.syncmode} \
188 --gcmode ${cfg.gcmode} \
189 --port ${toString cfg.port} \
190 --maxpeers ${toString cfg.maxpeers} \
191 ${lib.optionalString cfg.http.enable ''--http --http.addr ${cfg.http.address} --http.port ${toString cfg.http.port}''} \
192 ${lib.optionalString (cfg.http.apis != null) ''--http.api ${lib.concatStringsSep "," cfg.http.apis}''} \
193 ${lib.optionalString cfg.websocket.enable ''--ws --ws.addr ${cfg.websocket.address} --ws.port ${toString cfg.websocket.port}''} \
194 ${lib.optionalString (cfg.websocket.apis != null) ''--ws.api ${lib.concatStringsSep "," cfg.websocket.apis}''} \
195 ${lib.optionalString cfg.metrics.enable ''--metrics --metrics.addr ${cfg.metrics.address} --metrics.port ${toString cfg.metrics.port}''} \
196 --authrpc.addr ${cfg.authrpc.address} --authrpc.port ${toString cfg.authrpc.port} --authrpc.vhosts ${lib.concatStringsSep "," cfg.authrpc.vhosts} \
197 ${if (cfg.authrpc.jwtsecret != "") then ''--authrpc.jwtsecret ${cfg.authrpc.jwtsecret}'' else ''--authrpc.jwtsecret ${dataDir}/geth/jwtsecret''} \
198 ${lib.escapeShellArgs cfg.extraArgs} \