git.iko.soy/typeable/nixpkgs
on master help sign in
nixos/geth: initial service
commit3ec9637a059d310f68c1014f946adacd97c89394 authorPascal Bach <pascal.bach@nextrem.ch> dateSun 22 Nov 2020, 17:35 (6 years ago) parentcad2824
3 files changed+220
nixos/modules/module-list.nix+1 nixos/modules/services/blockchain/ethereum/geth.nix+178 nixos/tests/geth.nix+41
nixos/modules/module-list.nix+1
@@ -256,6 +256,7 @@
256256 ./services/backup/tsm.nix
257257 ./services/backup/zfs-replication.nix
258258 ./services/backup/znapzend.nix
259+ ./services/blockchain/ethereum/geth.nix
259260 ./services/cluster/hadoop/default.nix
260261 ./services/cluster/k3s/default.nix
261262 ./services/cluster/kubernetes/addons/dns.nix
nixos/modules/services/blockchain/ethereum/geth.nixnew file+178
@@ -0,0 +1,178 @@
1+{ config, lib, pkgs, ... }:
2+
3+with lib;
4+
5+let
6+ eachGeth = config.services.geth;
7+
8+ gethOpts = { config, lib, name, ...}: {
9+
10+ options = {
11+
12+ enable = lib.mkEnableOption "Go Ethereum Node";
13+
14+ port = mkOption {
15+ type = types.port;
16+ default = 30303;
17+ description = "Port number Go Ethereum will be listening on, both TCP and UDP.";
18+ };
19+
20+ http = {
21+ enable = lib.mkEnableOption "Go Ethereum HTTP API";
22+ address = mkOption {
23+ type = types.str;
24+ default = "127.0.0.1";
25+ description = "Listen address of Go Ethereum HTTP API.";
26+ };
27+
28+ port = mkOption {
29+ type = types.port;
30+ default = 8545;
31+ description = "Port number of Go Ethereum HTTP API.";
32+ };
33+
34+ apis = mkOption {
35+ type = types.nullOr (types.listOf types.str);
36+ default = null;
37+ description = "APIs to enable over WebSocket";
38+ example = ["net" "eth"];
39+ };
40+ };
41+
42+ websocket = {
43+ enable = lib.mkEnableOption "Go Ethereum WebSocket API";
44+ address = mkOption {
45+ type = types.str;
46+ default = "127.0.0.1";
47+ description = "Listen address of Go Ethereum WebSocket API.";
48+ };
49+
50+ port = mkOption {
51+ type = types.port;
52+ default = 8546;
53+ description = "Port number of Go Ethereum WebSocket API.";
54+ };
55+
56+ apis = mkOption {
57+ type = types.nullOr (types.listOf types.str);
58+ default = null;
59+ description = "APIs to enable over WebSocket";
60+ example = ["net" "eth"];
61+ };
62+ };
63+
64+ metrics = {
65+ enable = lib.mkEnableOption "Go Ethereum prometheus metrics";
66+ address = mkOption {
67+ type = types.str;
68+ default = "127.0.0.1";
69+ description = "Listen address of Go Ethereum metrics service.";
70+ };
71+
72+ port = mkOption {
73+ type = types.port;
74+ default = 6060;
75+ description = "Port number of Go Ethereum metrics service.";
76+ };
77+ };
78+
79+ network = mkOption {
80+ type = types.nullOr (types.enum [ "goerli" "rinkeby" "yolov2" "ropsten" ]);
81+ default = null;
82+ description = "The network to connect to. Mainnet (null) is the default ethereum network.";
83+ };
84+
85+ syncmode = mkOption {
86+ type = types.enum [ "fast" "full" "light" ];
87+ default = "fast";
88+ description = "Blockchain sync mode.";
89+ };
90+
91+ gcmode = mkOption {
92+ type = types.enum [ "full" "archive" ];
93+ default = "full";
94+ description = "Blockchain garbage collection mode.";
95+ };
96+
97+ maxpeers = mkOption {
98+ type = types.int;
99+ default = 50;
100+ description = "Maximum peers to connect to.";
101+ };
102+
103+ extraArgs = mkOption {
104+ type = types.listOf types.str;
105+ description = "Additional arguments passed to Go Ethereum.";
106+ default = [];
107+ };
108+
109+ package = mkOption {
110+ default = pkgs.go-ethereum.geth;
111+ type = types.package;
112+ description = "Package to use as Go Ethereum node.";
113+ };
114+ };
115+ };
116+in
117+
118+{
119+
120+ ###### interface
121+
122+ options = {
123+ services.geth = mkOption {
124+ type = types.attrsOf (types.submodule gethOpts);
125+ default = {};
126+ description = "Specification of one or more geth instances.";
127+ };
128+ };
129+
130+ ###### implementation
131+
132+ config = mkIf (eachGeth != {}) {
133+
134+ environment.systemPackages = flatten (mapAttrsToList (gethName: cfg: [
135+ cfg.package
136+ ]) eachGeth);
137+
138+ systemd.services = mapAttrs' (gethName: cfg: (
139+ nameValuePair "geth-${gethName}" (mkIf cfg.enable {
140+ description = "Go Ethereum node (${gethName})";
141+ wantedBy = [ "multi-user.target" ];
142+ after = [ "network.target" ];
143+
144+ serviceConfig = {
145+ DynamicUser = true;
146+ Restart = "always";
147+ StateDirectory = "goethereum/${gethName}/${if (cfg.network == null) then "mainnet" else cfg.network}";
148+
149+ # Hardening measures
150+ PrivateTmp = "true";
151+ ProtectSystem = "full";
152+ NoNewPrivileges = "true";
153+ PrivateDevices = "true";
154+ MemoryDenyWriteExecute = "true";
155+ };
156+
157+ script = ''
158+ ${cfg.package}/bin/geth \
159+ --nousb \
160+ --ipcdisable \
161+ ${optionalString (cfg.network != null) ''--${cfg.network}''} \
162+ --syncmode ${cfg.syncmode} \
163+ --gcmode ${cfg.gcmode} \
164+ --port ${toString cfg.port} \
165+ --maxpeers ${toString cfg.maxpeers} \
166+ ${if cfg.http.enable then ''--http --http.addr ${cfg.http.address} --http.port ${toString cfg.http.port}'' else ""} \
167+ ${optionalString (cfg.http.apis != null) ''--http.api ${lib.concatStringsSep "," cfg.http.apis}''} \
168+ ${if cfg.websocket.enable then ''--ws --ws.addr ${cfg.websocket.address} --ws.port ${toString cfg.websocket.port}'' else ""} \
169+ ${optionalString (cfg.websocket.apis != null) ''--ws.api ${lib.concatStringsSep "," cfg.websocket.apis}''} \
170+ ${optionalString cfg.metrics.enable ''--metrics --metrics.addr ${cfg.metrics.address} --metrics.port ${toString cfg.metrics.port}''} \
171+ ${lib.escapeShellArgs cfg.extraArgs} \
172+ --datadir /var/lib/goethereum/${gethName}/${if (cfg.network == null) then "mainnet" else cfg.network}
173+ '';
174+ }))) eachGeth;
175+
176+ };
177+
178+}
nixos/tests/geth.nixnew file+41
@@ -0,0 +1,41 @@
1+import ./make-test-python.nix ({ pkgs, ... }: {
2+ name = "geth";
3+ meta = with pkgs.lib; {
4+ maintainers = with maintainers; [bachp ];
5+ };
6+
7+ machine = { ... }: {
8+ services.geth."mainnet" = {
9+ enable = true;
10+ http = {
11+ enable = true;
12+ };
13+ };
14+ services.geth."testnet" = {
15+ enable = true;
16+ port = 30304;
17+ network = "goerli";
18+ http = {
19+ enable = true;
20+ port = 18545;
21+ };
22+ };
23+ };
24+
25+ testScript = ''
26+ start_all()
27+
28+ machine.wait_for_unit("geth-mainnet.service")
29+ machine.wait_for_unit("geth-testnet.service")
30+ machine.wait_for_open_port(8545)
31+ machine.wait_for_open_port(18545)
32+
33+ machine.succeed(
34+ 'geth attach --exec "eth.chainId()" http://localhost:8545 | grep \'"0x0"\' '
35+ )
36+
37+ machine.succeed(
38+ 'geth attach --exec "eth.chainId()" http://localhost:18545 | grep \'"0x5"\' '
39+ )
40+ '';
41+})