Skip to content

Commit

Permalink
nimbus-beacon-node: init service
Browse files Browse the repository at this point in the history
Nimbus beacon node is an Ethereum consensus layer client.
Should be deployed together with an execution layer client like Geth.

Signed-off-by: Jakub Sokołowski <jakub@status.im>
  • Loading branch information
jakubgs committed Dec 8, 2022
1 parent 869fa31 commit 9e87264
Show file tree
Hide file tree
Showing 6 changed files with 408 additions and 0 deletions.
7 changes: 7 additions & 0 deletions nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
Expand Up @@ -37,6 +37,13 @@
<link linkend="opt-programs.fzf.fuzzyCompletion">programs.fzf</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://nimbus.guide/">nimbus-beacon-node</link>,
an Ethereum consensus layer client available as
<link xlink:href="options.html#opt-services.nimbus-beacon-node.enable">services.nimbus-beacon-node</link>.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-23.05-incompatibilities">
Expand Down
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2305.section.md
Expand Up @@ -18,6 +18,8 @@ In addition to numerous new and upgraded packages, this release has the followin

- [fzf](https://github.com/junegunn/fzf), a command line fuzzyfinder. Available as [programs.fzf](#opt-programs.fzf.fuzzyCompletion).

- [nimbus-beacon-node](https://nimbus.guide/), an Ethereum consensus layer client available as [services.nimbus-beacon-node](options.html#opt-services.nimbus-beacon-node.enable).

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

<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Expand Up @@ -329,6 +329,7 @@
./services/blockchain/ethereum/geth.nix
./services/blockchain/ethereum/erigon.nix
./services/blockchain/ethereum/lighthouse.nix
./services/blockchain/nimbus/beacon-node.nix
./services/backup/zrepl.nix
./services/cluster/corosync/default.nix
./services/cluster/hadoop/default.nix
Expand Down
64 changes: 64 additions & 0 deletions nixos/modules/services/blockchain/nimbus/beacon-node.md
@@ -0,0 +1,64 @@
# Nimbus Beacon Node {#module-services-nimbus-beacon-node}

# Quick Start {#module-services-nimbus-beacon-node-quick-start}
Nimbus is an extremely efficient consensus layer client implementation.
While it's optimised for embedded systems and resource-restricted devices --
including Raspberry Pis, its low resource usage also makes it an excellent choice
for any server or desktop (where it simply takes up fewer resources).

In order for the [Consensus Layer(CL) client](https://ethereum.org/en/developers/docs/nodes-and-clients/#consensus-clients)
to function it requires access to an [Execution Layer(EL) client](https://ethereum.org/en/developers/docs/nodes-and-clients/#execution-clients)
listening for AuthRPC requests on `http://localhost:8551/`.
An example configuration using Geth as the EL client would look like this:

```nix
# Execution layer client
services.geth.mainnet = {
enable = true;
authrpc = {
enable = true;
port = 8551;
vhosts = ["localhost"];
jwtsecret = "/var/run/geth/jwtsecret";
};
};
# Consensus layer client
services.nimbus-beacon-node = {
enable = true;
web3Urls = ["http://localhost:${toString config.services.geth.mainnet.authrpc.port}"];
jwtSecret = "/var/run/geth/jwtsecret";
rest.enable = true;
};
```
::: {.warning}
This assumes you have a `/var/run/geth/jwtsecret` containing a 64 byte secret.
You can create such a secret using `openssl rand -hex 32 | tr -d "\n"`.
:::

This should allow the CL node to communicate with EL node via Auth RPC.

Keep in mind that the JWT secret needs to be the same and readable for both nodes.
If both services run on the same host and listen on `localhost` it's just a formality.

You can check if the node is working using the REST API:

{command}`curl -s localhost:5052/eth/v1/node/version`
```json
{"data":{"version":"Nimbus/v22.9.1-ad286b-stateofus"}}
```
{command}`curl -s localhost:5052/eth/v1/node/syncing`
```json
{"data":{"head_slot":"45102","sync_distance":"4744542","is_syncing":true,"is_optimistic":false}}
```

# Configuration {#module-services-nimbus-beacon-node-configuration}

Other settings worth looking at include:

* {option}`services.nimbus-beacon-node.suggestedFeeRecipient` - Your address for receiving [transaction fees](https://nimbus.guide/suggested-fee-recipient.html).
* {option}`services.nimbus-beacon-node.doppelganger` - Miss two epochs to avoid [validator slashing](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/rewards-and-penalties/#slashing).
* {option}`services.nimbus-beacon-node.metrics.enable` - Prometheus metrics endpoint listening on `9100` by default.
* {option}`services.nimbus-beacon-node.extraArgs` - Ability to provide flags not defined in the service.

For documentation see: <https://nimbus.guide/>
236 changes: 236 additions & 0 deletions nixos/modules/services/blockchain/nimbus/beacon-node.nix
@@ -0,0 +1,236 @@
{ config, lib, pkgs, ... }:

let
inherit (lib)
escapeShellArg mdDoc literalMD concatStringsSep mkMerge
toUpper boolToString length forEach optionals optionalAttrs
types mkEnableOption mkOption mkIf literalExpression;

cfg = config.services.nimbus-beacon-node;
in {
options = {
services = {
nimbus-beacon-node = {
enable = mkEnableOption (mdDoc "Nimbus Beacon Node service");

package = mkOption {
type = types.package;
default = pkgs.nimbus;
defaultText = literalExpression "pkgs.nimbus";
description = mdDoc "Package to use as Go Ethereum node.";
};

service = {
user = mkOption {
type = types.str;
default = "nimbus";
description = mdDoc "Username for Nimbus beacon node service.";
};

group = mkOption {
type = types.str;
default = "nimbus";
description = mdDoc "Group name for Nimbus beacon node service.";
};
};

dataDir = mkOption {
type = types.path;
default = "/var/lib/nimbus-beacon-node";
description = mdDoc "Directory for Nimbus beacon node blockchain data.";
};

network = mkOption {
type = types.str;
default = "mainnet";
description = mdDoc "Name of beacon node network to connect to.";
};

log = {
level = mkOption {
type = types.enum ["trace" "debug" "info" "notice" "warn" "error" "fatal" "none"];
default = "info";
example = "debug";
description = mdDoc "Logging level.";
};

format = mkOption {
type = types.enum ["auto" "colors" "nocolors" "json"];
default = "auto";
example = "json";
description = mdDoc "Logging formatting.";
};
};

web3Urls = mkOption {
type = types.listOf types.str;
default = [];
example = ["http://localhost:8551/"];
description = mdDoc "Mandatory URL(s) for the Web3 RPC endpoints.";
};

jwtSecret = mkOption {
type = types.path;
default = null;
example = "/var/run/nimbus/jwtsecret";
description = mdDoc ''
Path of file with 32 bytes long JWT secret for Auth RPC endpoint.
Can be generated using 'openssl rand -hex 32'.
'';
};

subAllSubnets = mkOption {
type = types.bool;
default = false;
description = mdDoc "Subscribe to all attestation subnet topics.";
};

doppelganger = mkOption {
type = types.bool;
default = true;
description = mdDoc ''
Protection against slashing due to double-voting.
Means you will miss two attestations when restarting.
'';
};

suggestedFeeRecipient = mkOption {
type = types.nullOr types.str;
default = null;
description = mdDoc ''
Wallet address where transaction fee tips - priority fees,
unburnt portion of gas fees - will be sent.
'';
};

listenPort = mkOption {
type = types.port;
default = 9000;
description = mdDoc "Listen port for libp2p protocol.";
};

discoverPort = mkOption {
type = types.port;
default = 9000;
description = mdDoc "Listen port for libp2p protocol.";
};

nat = mkOption {
type = types.str;
default = "any";
example = "extip:12.34.56.78";
description = literalMD ''
Method for determining public address. (any, none, upnp, pmp, extip:<IP>)
'';
};

metrics = {
enable = lib.mkEnableOption (mdDoc "Nimbus beacon node metrics endpoint");
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = mdDoc "Metrics address for beacon node.";
};
port = mkOption {
type = types.port;
default = 9100;
description = mdDoc "Metrics port for beacon node.";
};
};

rest = {
enable = lib.mkEnableOption (mdDoc "Nimbus beacon node REST API");
address = mkOption {
type = types.str;
default = "127.0.0.1";
description = mdDoc "Listening address of the REST API server.";
};

port = mkOption {
type = types.port;
default = 5052;
description = mdDoc "Port for the REST API server.";
};
};

extraArgs = mkOption {
type = types.listOf types.str;
default = [];
example = ["--num-threads=1" "--graffiti=1337_h4x0r"];
description = mdDoc "Additional arguments passed to node.";
};
};
};
};

config = mkIf cfg.enable {
assertions = [
{ assertion = length cfg.web3Urls > 0;
message = "Nimbus beacon node requires at least one Web3 URL in services.nimbus-beacon-node.web3Urls to work!"; }
];

users.users = optionalAttrs (cfg.service.user == "nimbus") {
nimbus = {
group = cfg.service.group;
home = cfg.dataDir;
description = "Nimbus beacon node service user";
isSystemUser = true;
};
};

users.groups = optionalAttrs (cfg.service.user == "nimbus") {
nimbus = { };
};

systemd.services.nimbus-beacon-node = {
description = "Nimbus Beacon Node (Ethereum consensus client)";

serviceConfig = mkMerge [{
User = cfg.service.user;
Group = cfg.service.group;

ExecStart = concatStringsSep " \\\n " ([
"${cfg.package}/bin/nimbus_beacon_node"
"--network=${cfg.network}"
"--data-dir=${cfg.dataDir}"
"--log-level=${toUpper cfg.log.level}"
"--log-format=${cfg.log.format}"
"--nat=${cfg.nat}"
"--tcp-port=${toString cfg.listenPort}"
"--udp-port=${toString cfg.discoverPort}"
"--subscribe-all-subnets=${boolToString cfg.subAllSubnets}"
"--doppelganger-detection=${boolToString cfg.doppelganger}"
"--rest=${boolToString cfg.rest.enable}"
] ++ optionals cfg.rest.enable [
"--rest-address=${cfg.rest.address}"
"--rest-port=${toString cfg.rest.port}"
] ++ [
"--metrics=${boolToString cfg.metrics.enable}"
] ++ optionals cfg.metrics.enable [
"--metrics-address=${cfg.metrics.address}"
"--metrics-port=${toString cfg.metrics.port}"
] ++ (forEach cfg.web3Urls (u: "--web3-url=${u}"))
++ optionals (cfg.jwtSecret != null) ["--jwt-secret=${cfg.jwtSecret}"]
++ optionals (cfg.suggestedFeeRecipient != null) ["--suggested-fee-recipient=${cfg.suggestedFeeRecipient}"]
++ (forEach cfg.extraArgs escapeShellArg));

WorkingDirectory = cfg.dataDir;
TimeoutSec = 1200;
Restart = "on-failure";
# Don't restart when Doppelganger detection has been activated
RestartPreventExitStatus = 129;
}
(mkIf (cfg.dataDir == "/var/lib/nimbus-beacon-node") {
StateDirectory = "nimbus-beacon-node";
StateDirectoryMode = "0750";
})];
wantedBy = [ "multi-user.target" ];
requires = [ "network.target" ];
};
};

meta = {
doc = ./beacon-node.xml;
maintainers = with lib.maintainers; [ jakubgs ];
};
}

0 comments on commit 9e87264

Please sign in to comment.