diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml index 61daeac86a64d50..4ad006b0b5b13a3 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml @@ -37,6 +37,13 @@ programs.fzf. + + + nimbus-beacon-node, + an Ethereum consensus layer client available as + services.nimbus-beacon-node. + +
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index f9c76b02f891c63..3f29e66dae6d2ee 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -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} diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 24dd30e1575018e..0724004d685e25b 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -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 diff --git a/nixos/modules/services/blockchain/nimbus/beacon-node.md b/nixos/modules/services/blockchain/nimbus/beacon-node.md new file mode 100644 index 000000000000000..5371b5909a81d56 --- /dev/null +++ b/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: diff --git a/nixos/modules/services/blockchain/nimbus/beacon-node.nix b/nixos/modules/services/blockchain/nimbus/beacon-node.nix new file mode 100644 index 000000000000000..b883acb0f1e2a80 --- /dev/null +++ b/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:) + ''; + }; + + 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 ]; + }; +} diff --git a/nixos/modules/services/blockchain/nimbus/beacon-node.xml b/nixos/modules/services/blockchain/nimbus/beacon-node.xml new file mode 100644 index 000000000000000..cafa032932c299c --- /dev/null +++ b/nixos/modules/services/blockchain/nimbus/beacon-node.xml @@ -0,0 +1,98 @@ + + Nimbus Beacon Node + + Source: + modules/services/blockchain/nimbus/beacon-node.nix + + + Upstream documentation: + + +
+ Quickstart + + 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) node to function it requires access to an + Execution Layer(EL) client listening for AuthRPC requests on http://localhost:8551/. + An example configuration using Geth as the EL client would look like this: + + # Execution layer client + = { + 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; + }; + + + + This assumes you have a /var/run/geth/jwtsecret containing a 32 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 noded 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: + + $ curl -s localhost:5052/eth/v1/node/version + {"data":{"version":"Nimbus/v22.9.1-ad286b-stateofus"}} + $ curl -s localhost:5052/eth/v1/node/syncing + {"data":{"head_slot":"45102","sync_distance":"4744542","is_syncing":true,"is_optimistic":false}} + + +
+
+ Configuration + + Other settings worth looking at include: + + + + + suggestedFeeRecipient - Your address for receiving transaction fees. + + + + + doppelganger - Miss two epochs to avoid validator slashing. + + + + + metrics.enable - Prometheus metrics endpoint listening on 9100 by default. + + + + + extraArgs - Ability to provide flags not defined in the service. + + + +
+