grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / blockchain / ethereum / geth.nix
blobadf9fc1db32a84ed5e13163e94e910be3fedf50f
1 { config, lib, pkgs, ... }:
2 let
3   eachGeth = config.services.geth;
5   gethOpts = { config, lib, name, ...}: {
7     options = {
9       enable = lib.mkEnableOption "Go Ethereum Node";
11       port = lib.mkOption {
12         type = lib.types.port;
13         default = 30303;
14         description = "Port number Go Ethereum will be listening on, both TCP and UDP.";
15       };
17       http = {
18         enable = lib.mkEnableOption "Go Ethereum HTTP API";
19         address = lib.mkOption {
20           type = lib.types.str;
21           default = "127.0.0.1";
22           description = "Listen address of Go Ethereum HTTP API.";
23         };
25         port = lib.mkOption {
26           type = lib.types.port;
27           default = 8545;
28           description = "Port number of Go Ethereum HTTP API.";
29         };
31         apis = lib.mkOption {
32           type = lib.types.nullOr (lib.types.listOf lib.types.str);
33           default = null;
34           description = "APIs to enable over WebSocket";
35           example = ["net" "eth"];
36         };
37       };
39       websocket = {
40         enable = lib.mkEnableOption "Go Ethereum WebSocket API";
41         address = lib.mkOption {
42           type = lib.types.str;
43           default = "127.0.0.1";
44           description = "Listen address of Go Ethereum WebSocket API.";
45         };
47         port = lib.mkOption {
48           type = lib.types.port;
49           default = 8546;
50           description = "Port number of Go Ethereum WebSocket API.";
51         };
53         apis = lib.mkOption {
54           type = lib.types.nullOr (lib.types.listOf lib.types.str);
55           default = null;
56           description = "APIs to enable over WebSocket";
57           example = ["net" "eth"];
58         };
59       };
61       authrpc = {
62         enable = lib.mkEnableOption "Go Ethereum Auth RPC API";
63         address = lib.mkOption {
64           type = lib.types.str;
65           default = "127.0.0.1";
66           description = "Listen address of Go Ethereum Auth RPC API.";
67         };
69         port = lib.mkOption {
70           type = lib.types.port;
71           default = 8551;
72           description = "Port number of Go Ethereum Auth RPC API.";
73         };
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"];
80         };
82         jwtsecret = lib.mkOption {
83           type = lib.types.str;
84           default = "";
85           description = "Path to a JWT secret for authenticated RPC endpoint.";
86           example = "/var/run/geth/jwtsecret";
87         };
88       };
90       metrics = {
91         enable = lib.mkEnableOption "Go Ethereum prometheus metrics";
92         address = lib.mkOption {
93           type = lib.types.str;
94           default = "127.0.0.1";
95           description = "Listen address of Go Ethereum metrics service.";
96         };
98         port = lib.mkOption {
99           type = lib.types.port;
100           default = 6060;
101           description = "Port number of Go Ethereum metrics service.";
102         };
103       };
105       network = lib.mkOption {
106         type = lib.types.nullOr (lib.types.enum [ "goerli" "rinkeby" "yolov2" "ropsten" ]);
107         default = null;
108         description = "The network to connect to. Mainnet (null) is the default ethereum network.";
109       };
111       syncmode = lib.mkOption {
112         type = lib.types.enum [ "snap" "fast" "full" "light" ];
113         default = "snap";
114         description = "Blockchain sync mode.";
115       };
117       gcmode = lib.mkOption {
118         type = lib.types.enum [ "full" "archive" ];
119         default = "full";
120         description = "Blockchain garbage collection mode.";
121       };
123       maxpeers = lib.mkOption {
124         type = lib.types.int;
125         default = 50;
126         description = "Maximum peers to connect to.";
127       };
129       extraArgs = lib.mkOption {
130         type = lib.types.listOf lib.types.str;
131         description = "Additional arguments passed to Go Ethereum.";
132         default = [];
133       };
135       package = lib.mkPackageOption pkgs [ "go-ethereum" "geth" ] { };
136     };
137   };
142   ###### interface
144   options = {
145     services.geth = lib.mkOption {
146       type = lib.types.attrsOf (lib.types.submodule gethOpts);
147       default = {};
148       description = "Specification of one or more geth instances.";
149     };
150   };
152   ###### implementation
154   config = lib.mkIf (eachGeth != {}) {
156     environment.systemPackages = lib.flatten (lib.mapAttrsToList (gethName: cfg: [
157       cfg.package
158     ]) eachGeth);
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}";
163     in (
164       lib.nameValuePair "geth-${gethName}" (lib.mkIf cfg.enable {
165       description = "Go Ethereum node (${gethName})";
166       wantedBy = [ "multi-user.target" ];
167       after = [ "network.target" ];
169       serviceConfig = {
170         DynamicUser = true;
171         Restart = "always";
172         StateDirectory = stateDir;
174         # Hardening measures
175         PrivateTmp = "true";
176         ProtectSystem = "full";
177         NoNewPrivileges = "true";
178         PrivateDevices = "true";
179         MemoryDenyWriteExecute = "true";
180       };
182       script = ''
183         ${cfg.package}/bin/geth \
184           --nousb \
185           --ipcdisable \
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} \
199           --datadir ${dataDir}
200       '';
201     }))) eachGeth;
203   };