Skip to content

Commit

Permalink
Add nixos service and scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
jbgi authored and rvl committed Jan 17, 2022
1 parent 2fdc9a5 commit 24088cf
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -47,3 +47,6 @@ hie.yaml

### Docs build
/_build/

### databases of scripts
/db-cardano-wallet-*
2 changes: 1 addition & 1 deletion lib/cli/src/Cardano/CLI.hs
Expand Up @@ -1186,7 +1186,7 @@ hostPreferenceOption = option str $ mempty
<> long "listen-address"
<> metavar "HOST"
<> help
("Specification of which host to the bind API server to. " <>
("Specification of which host to bind the API server to. " <>
"Can be an IPv[46] address, hostname, or '*'.")
<> value "127.0.0.1"
<> showDefaultWith (const "127.0.0.1")
Expand Down
197 changes: 197 additions & 0 deletions nix/nixos/cardano-wallet-service.nix
@@ -0,0 +1,197 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.cardano-wallet;
inherit (lib) mkIf mkEnableOption mkOption types;
logLevels = types.enum [ "debug" "info" "notice" "warning" "error" "critical" "alert" "emergency" "off" ];
in
{
options.services.cardano-wallet = {

enable = mkEnableOption "Cardano Wallet service";

serverArgs = mkOption {
description = "Command-line to launch cardano-wallet server.";
type = types.separatedString " ";
default = lib.concatStringsSep " " ([
"--listen-address"
(lib.escapeShellArg cfg.listenAddress)
"--port"
(toString cfg.port)
"--node-socket"
(lib.escapeShellArg "$CARDANO_NODE_SOCKET_PATH")
"--sync-tolerance"
"${toString cfg.syncTolerance}s"
"--log-level"
cfg.logLevel
"--pool-metadata-fetching"
(if (cfg.poolMetadataFetching.enable)
then
(if cfg.poolMetadataFetching.smashUrl != null
then cfg.poolMetadataFetching.smashUrl else "direct")
else "none")
"--${cfg.walletMode}"
] ++ lib.optional (cfg.walletMode != "mainnet")
(lib.escapeShellArg cfg.genesisFile)
++ lib.optionals (cfg.tokenMetadataServer != null)
[ "--token-metadata-server" cfg.tokenMetadataServer ]
++ lib.optionals (cfg.database != null)
[ "--database" (lib.escapeShellArg cfg.database) ]
++ lib.mapAttrsToList
(name: level: "--trace-${name}=${level}")
cfg.trace
);
};

command = mkOption {
type = types.str;
internal = true;
default = lib.concatStringsSep " " ([
"${cfg.package}/bin/${cfg.package.exeName}"
"serve"
cfg.serverArgs
] ++ lib.optionals (cfg.rtsOpts != "") [ "+RTS" cfg.rtsOpts "-RTS" ]);
};

package = mkOption {
type = types.package;
default = ((import ../.. { }).legacyPackages.${pkgs.system}).hsPkgs.cardano-wallet.components.exes.cardano-wallet;
description = "Package for the cardano wallet executable.";
};

genesisFile = mkOption {
type = types.nullOr (types.either types.str types.path);
default = null;
description = "Path to genesis file, if not running on mainnet.";
};

listenAddress = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Which host to bind the API server to.";
};

port = mkOption {
type = types.port;
default = 8090;
description = "The port on which the cardano-wallet HTTP API server will listen.";
};

nodeSocket = mkOption {
type = types.str;
default = "/run/cardano-node/node.socket";
description = ''Cardano-Node local communication socket path.'';
};

walletMode = mkOption {
type = types.enum [ "mainnet" "staging" "testnet" ];
default = "mainnet";
description = "Which mode to start wallet in: --mainnet, --staging or --testnet";
};

database = mkOption {
type = types.nullOr types.str;
default = "$STATE_DIRECTORY";
description = ''Directory for storing wallets.
Run in-memory if null.
Default to '/var/lib/cardano-wallet'.
'';
};

syncTolerance = mkOption {
type = types.ints.positive;
default = 300;
description = "Time duration within which we consider being synced with the network. Expressed in seconds.";
};

poolMetadataFetching = mkOption {
type = types.submodule {
options = {
enable = mkEnableOption "Stake pool metadata fetching.";
smashUrl = mkOption {
description = ''
URL of SMASH metadata proxy to use.
If null, metadata will be fetched directly from the
stake pool's URL.
'';
type = types.nullOr types.str;
default = null;
};
};
};
default = { enable = false; };
example = {
enable = true;
smashUrl = "https://smash.cardano-mainnet.iohk.io";
};
};

tokenMetadataServer = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Sets the URL of the token metadata server,
default to 'metadataUrl' of the 'environnement' attribute, if exists.
If unset, metadata will not be fetched. By using this
option, you are fully trusting the operator of the
metadata server to provide authentic token metadata.
'';
};

logLevel = mkOption {
type = logLevels;
default = "info";
description = "Global minimum severity for a message to be logged.";
};

trace = mkOption {
type = types.attrsOf logLevels;
default = { };
description = ''
For each tracer, minimum severity for a message to be logged, or
"off" to disable the tracer".
'';
};

rtsOpts = mkOption {
type = types.separatedString " ";
default = "-N2";
example = "-M2G -N4";
description = ''
GHC runtime-system options for the cardano-wallet process.
See https://downloads.haskell.org/ghc/8.10.7/docs/html/users_guide/runtime_control.html#setting-rts-options-on-the-command-line for documentation.
'';
};

};

config = mkIf cfg.enable {

assertions = [
{
assertion = (cfg.walletMode == "mainnet") == (cfg.genesisFile == null);
message = ''The option services.cardano-wallet.genesisFile must be set
if, and only if, services.cardano-wallet.walletMode is not \"mainnet\".
'';
}
{
assertion = !(lib.hasPrefix "/" cfg.database || lib.hasPrefix ".." cfg.database);
message = "The option services.cardano-node.database should be a relative path (of /var/lib/).";
}
];

systemd.services.cardano-wallet = {
description = "cardano-wallet daemon";
wantedBy = [ "multi-user.target" ];

serviceConfig = {
DynamicUser = true;
ExecStart = cfg.command;
StateDirectory = if cfg.database == "$STATE_DIRECTORY" then "cardano-wallet" else cfg.database;
};

environment = {
CARDANO_NODE_SOCKET_PATH = cfg.nodeSocket;
};
};
};
}
1 change: 1 addition & 0 deletions nix/nixos/default.nix
@@ -0,0 +1 @@
{ imports = import ./module-list.nix; }
3 changes: 3 additions & 0 deletions nix/nixos/module-list.nix
@@ -0,0 +1,3 @@
[
./cardano-wallet-service.nix
]
17 changes: 17 additions & 0 deletions nix/nixos/tests/default.nix
@@ -0,0 +1,17 @@
{ pkgs
, project
}:
let
importTest = fn: args:
let
imported = import fn;
test = import (pkgs.path + "/nixos/tests/make-test-python.nix") imported;
in
test ({
inherit pkgs project;
inherit (pkgs) system config;
} // args);
in
{
basicTest = importTest ./service-basic-test.nix { };
}
59 changes: 59 additions & 0 deletions nix/nixos/tests/service-basic-test.nix
@@ -0,0 +1,59 @@
{ pkgs, project, lib, ... }: with pkgs;
let
inherit (project.hsPkgs.cardano-wallet.components.exes) cardano-wallet;
inherit (pkgs) cardanoLib;
in
{
name = "wallet-nixos-test";
nodes = {
machine = { config, ... }: {
nixpkgs.pkgs = pkgs;
imports = [
../.
(project.pkg-set.config.packages.cardano-node.src + "/nix/nixos")
];
services.cardano-wallet = {
enable = true;
package = cardano-wallet;
walletMode = "mainnet";
nodeSocket = config.services.cardano-node.socketPath;
poolMetadataFetching = {
enable = true;
smashUrl = cardanoLib.environments.mainnet.smashUrl;
};
tokenMetadataServer = cardanoLib.environments.mainnet.metadataUrl;
};
services.cardano-node = {
enable = true;
environment = "mainnet";
environments = { mainnet = { }; };
package = project.hsPkgs.cardano-node.components.exes.cardano-node;
inherit (cardanoLib.environments.mainnet) nodeConfig;
topology = cardanoLib.mkEdgeTopology {
port = 3001;
edgeNodes = [ "127.0.0.1" ];
};
systemdSocketActivation = true;
};
systemd.services.cardano-node.serviceConfig.Restart = lib.mkForce "no";
systemd.services.cardano-wallet = {
after = [ "cardano-node.service" ];
serviceConfig = {
Restart = "no";
SupplementaryGroups = "cardano-node";
};
};
};
};
testScript = ''
start_all()
machine.wait_for_unit("cardano-node.service")
machine.wait_for_open_port(3001)
machine.wait_for_unit("cardano-wallet.service")
machine.wait_for_open_port(8090)
machine.succeed(
"${cardano-wallet}/bin/cardano-wallet network information"
)
'';

}
9 changes: 6 additions & 3 deletions nix/release-build.nix
Expand Up @@ -11,7 +11,7 @@

with pkgs.lib;

pkgs.stdenv.mkDerivation rec {
let drv = pkgs.stdenv.mkDerivation rec {
name = "${exe.identifier.name}-${version}";
version = exe.identifier.version;
phases = [ "installPhase" ];
Expand All @@ -31,5 +31,8 @@ pkgs.stdenv.mkDerivation rec {
''));

meta.platforms = platforms.all;
passthru = optionalAttrs (backend != null) { inherit backend; };
}
passthru = {
exePath = drv + "/bin/cardano-wallet";
} // (optionalAttrs (backend != null) { inherit backend; });
};
in drv
69 changes: 69 additions & 0 deletions nix/scripts.nix
@@ -0,0 +1,69 @@
{ evalService
, project
, customConfigs
}:
with project.pkgs;
let
mkScript = envConfig:
let
service = evalService {
inherit pkgs customConfigs;
serviceName = "cardano-wallet";
modules = [
./nixos/cardano-wallet-service.nix
({ config, ... }: {
services.cardano-wallet = let cfg = config.services.cardano-wallet; in
{
package = lib.mkDefault project.hsPkgs.cardano-wallet.components.exes.cardano-wallet;
walletMode = lib.mkDefault ({ mainnet = "mainnet"; staging = "staging"; }.${envConfig.name} or "testnet");
genesisFile = lib.mkIf (cfg.walletMode != "mainnet")
(lib.mkDefault envConfig.nodeConfig.ByronGenesisFile);
database = lib.mkDefault "./db-cardano-wallet-${envConfig.name}";
poolMetadataFetching = lib.mkDefault {
enable = lib.mkDefault true;
smashUrl = lib.mkIf (envConfig ? smashUrl)
(lib.mkDefault envConfig.smashUrl);
};
tokenMetadataServer = lib.mkIf (envConfig ? metadataUrl) (lib.mkDefault envConfig.metadataUrl);
};
})
];
};

in
writeScriptBin "cardano-wallet-${envConfig.name}" ''
#!${pkgs.runtimeShell}
set -euo pipefail
exec ${service.command} $@
'';

debugDeps = with pkgs; [
coreutils
findutils
gnugrep
gnused
postgresql
strace
lsof
dnsutils
bashInteractive
iproute
curl
netcat
bat
tree
];

in
cardanoLib.forEnvironments (environment: lib.recurseIntoAttrs (
let wallet = mkScript environment;
in
{
inherit wallet;
} // lib.optionalAttrs stdenv.buildPlatform.isLinux {
wallet-debug = pkgs.symlinkJoin {
inherit (wallet) name;
paths = [ wallet ] ++ debugDeps;
};
}
))

0 comments on commit 24088cf

Please sign in to comment.