Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/lighthouse: initial lighthouse ethereum service #128984

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,18 @@
<link xlink:href="options.html#opt-services.seafile.enable">services.seafile</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://github.com/sigp/lighthouse">lighthouse</link>,
a Ethereum 2.0 client. Available as
<link xlink:href="options.html#opt-services.lighthouse">services.lighthouse</link>.
Can be used together with the
<link xlink:href="https://geth.ethereum.org/">geth</link>,
available as
<link xlink:href="options.html#opt-services.geth">services.geth</link>,
to run a node for staking ETH2.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-21.11-incompatibilities">
Expand Down
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2111.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ In addition to numerous new and upgraded packages, this release has the followin

- [seafile](https://www.seafile.com/en/home/), an open source file syncing & sharing software. Available as [services.seafile](options.html#opt-services.seafile.enable).

- [lighthouse](https://github.com/sigp/lighthouse), a Ethereum 2.0 client. Available as [services.lighthouse](options.html#opt-services.lighthouse). Can be used together with the [geth](https://geth.ethereum.org/), available as [services.geth](options.html#opt-services.geth), to run a node for staking ETH2.

## Backward Incompatibilities {#sec-release-21.11-incompatibilities}

- The `services.wakeonlan` option was removed, and replaced with `networking.interfaces.<name>.wakeOnLan`.
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@
./services/backup/zfs-replication.nix
./services/backup/znapzend.nix
./services/blockchain/ethereum/geth.nix
./services/blockchain/ethereum/lighthouse.nix
./services/backup/zrepl.nix
./services/cluster/hadoop/default.nix
./services/cluster/k3s/default.nix
Expand Down
283 changes: 283 additions & 0 deletions nixos/modules/services/blockchain/ethereum/lighthouse.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
{ config, lib, pkgs, ... }:

with lib;

let
eachLighthouse = config.services.lighthouse;

lighthouseOpts = { config, lib, name, ...}: {

options = {

beacon = mkOption {
description = "Beacon node";
type = types.submodule {
options = {
enable = lib.mkEnableOption "Lightouse Beacon node";

address = mkOption {
type = types.str;
default = "0.0.0.0";
description = ''
Listen address of Beacon node.
'';
};

port = mkOption {
type = types.port;
default = 9000;
description = ''
Port number the Beacon node will be listening on.
'';
};

http = {
port = mkOption {
type = types.port;
default = 5052;
description = ''
Port number of Beacon node RPC service.
'';
};

address = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Listen address of Beacon node RPC service.
'';
};
};

metrics = {
enable = lib.mkEnableOption "Beacon node prometheus metrics";
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Listen address of Beacon node metrics service.
'';
};

port = mkOption {
type = types.port;
default = 5054;
description = ''
Port number of Beacon node metrics service.
'';
};
};

eth1Endpoints = mkOption {
type = types.listOf types.str;
default = ["http://localhost:8545"];
description = ''
List of ETH1 nodes to connect to.
'';
};

extraArgs = mkOption {
type = types.str;
description = ''
Additional arguments passed to the lighthouse beacon command.
'';
default = "";
example = "";
};
};
};
};

validator = mkOption {
description = "Validator node";
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable Lightouse Validator node.";
};

beaconNodes = mkOption {
type = types.listOf types.str;
default = ["http://localhost:5052"];
description = ''
Beacon nodes to connect to.
'';
};

metrics = {
enable = lib.mkEnableOption "Validator node prometheus metrics";
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Listen address of Validator node metrics service.
'';
};

port = mkOption {
type = types.port;
default = 5056;
description = ''
Port number of Validator node metrics service.
'';
};
};

extraArgs = mkOption {
type = types.str;
description = ''
Additional arguments passed to the lighthouse validator command.
'';
default = "";
example = "";
};
};
};
};

network = mkOption {
type = types.enum [ "prater" "pyrmont" "mainnet" ];
default = "mainnet";
description = ''
The network to connect to. Mainnet is the default ethereum network.
'';
};

extraArgs = mkOption {
type = types.str;
description = ''
Additional arguments passed to every lighthouse command.
'';
default = "";
example = "";
};

package = mkOption {
default = pkgs.lighthouse-ethereum;
type = types.package;
description = ''
Package to use as Lighthouse node.
'';
};
};
};

lighthouse-wrapper = (lighthouseName: cfg:
pkgs.writeScriptBin "lh-${lighthouseName}" ''
#! ${pkgs.runtimeShell}
cd ${cfg.package}
sudo=exec
if [[ "$USER" != lighthouse-${lighthouseName}-validator ]]; then
sudo='exec /run/wrappers/bin/sudo -u lighthouse-${lighthouseName}-validator'
fi
$sudo \
${cfg.package}/bin/lighthouse --network ${cfg.network} --datadir /var/lib/lighthouse-validator/${lighthouseName}/${cfg.network} $*
''
);


in

{

###### interface

options = {
services.lighthouse = mkOption {
type = types.attrsOf (types.submodule lighthouseOpts);
default = {};
description = "Specification of one or more lighthouse instances.";
};
};

###### implementation

config = mkIf (eachLighthouse != {}) {

# Create a real user for the validator so we can interact with it
users.users = mapAttrs' (lighthouseName: cfg: (
nameValuePair "lighthouse-${lighthouseName}-validator" {
group = "lighthouse";
createHome = false;
description = "Lighthouse Validator User (${lighthouseName})";
home = "/var/lib/lighthouse-validator/${lighthouseName}";
isSystemUser = true;
packages = [ cfg.package ];
})) eachLighthouse;

users.groups.lighthouse = {};

# Create a wrapper script for each instance that has the proper data directory set
environment.systemPackages = flatten (mapAttrsToList lighthouse-wrapper eachLighthouse);

systemd.tmpfiles.rules = [
"d '/var/lib/lighthouse-validator' 0770 root lighthouse - -"
] ++ flatten (mapAttrsToList (lighthouseName: cfg: [
"d '/var/lib/lighthouse-validator/${lighthouseName}' 0700 lighthouse-${lighthouseName}-validator lighthouse - -"
]) eachLighthouse);

systemd.services = mapAttrs' (lighthouseName: cfg: (
nameValuePair "lighthouse-${lighthouseName}-beacon" (mkIf cfg.beacon.enable {
description = "Lighthouse Beacon node (${lighthouseName})";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];

serviceConfig = {
DynamicUser = true;
Restart = "always";
StateDirectory = "lighthouse-beacon/${lighthouseName}/${cfg.network}";

# Hardening measures
PrivateTmp = "true";
ProtectSystem = "full";
NoNewPrivileges = "true";
PrivateDevices = "true";
MemoryDenyWriteExecute = "true";
};

script = ''
${cfg.package}/bin/lighthouse beacon \
--disable-upnp \
--http --http-address ${cfg.beacon.http.address} --http-port ${toString cfg.beacon.http.port} \
--network ${cfg.network} \
--eth1-endpoints ${lib.concatStringsSep "," cfg.beacon.eth1Endpoints} \
--port ${toString cfg.beacon.port} --listen-address ${cfg.beacon.address} \
${optionalString cfg.beacon.metrics.enable ''--metrics --metrics-address ${cfg.beacon.metrics.address} --metrics-port ${toString cfg.beacon.metrics.port}''} \
${cfg.extraArgs} ${cfg.beacon.extraArgs} \
--datadir /var/lib/lighthouse-beacon/${lighthouseName}/${cfg.network}
'';
}))) eachLighthouse // mapAttrs' (lighthouseName: cfg: (
nameValuePair "lighthouse-${lighthouseName}-validator" (mkIf cfg.validator.enable {
description = "Lighthouse Validator node (${lighthouseName})";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];

serviceConfig = {
User = "lighthouse-${lighthouseName}-validator";
Group = "lighthouse";
Restart = "always";
StateDirectory = "lighthouse-validator/${lighthouseName}/${cfg.network}";

# Hardening measures
PrivateTmp = "true";
ProtectSystem = "full";
NoNewPrivileges = "true";
PrivateDevices = "true";
MemoryDenyWriteExecute = "true";
};

script = ''
${cfg.package}/bin/lighthouse validator \
--network ${cfg.network} \
--beacon-nodes ${lib.concatStringsSep "," cfg.validator.beaconNodes} \
${optionalString cfg.validator.metrics.enable ''--metrics --metrics-address ${cfg.validator.metrics.address} --metrics-port ${toString cfg.validator.metrics.port}''} \
${cfg.extraArgs} ${cfg.validator.extraArgs} \
--datadir /var/lib/lighthouse-validator/${lighthouseName}/${cfg.network}
'';
}))) eachLighthouse;

};

}
Loading