From c79aaf9695776911927dcc684ea862bebeec0aef Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 3 Dec 2018 21:43:15 +0000 Subject: [PATCH] Add liquid --- configuration.nix | 2 + modules/clightning.nix | 2 +- modules/liquid.nix | 222 +++++++++++++++++++++++++++++++++++++++++ modules/nixbitcoin.nix | 11 +- pkgs/liquidd.nix | 45 +++++++++ 5 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 modules/liquid.nix create mode 100644 pkgs/liquidd.nix diff --git a/configuration.nix b/configuration.nix index bbe0621..80e9997 100644 --- a/configuration.nix +++ b/configuration.nix @@ -8,6 +8,7 @@ let nodeinfo = (import pkgs/nodeinfo.nix); lightning-charge = import pkgs/lightning-charge.nix { inherit pkgs; }; nanopos = import pkgs/nanopos.nix { inherit pkgs; }; + liquidd = import pkgs/liquidd.nix; in { disabledModules = [ "services/security/tor.nix" ]; @@ -30,6 +31,7 @@ in { inherit nodeinfo; inherit lightning-charge; inherit nanopos; + liquidd = (pkgs.callPackage liquidd { }); }; services.openssh.enable = true; diff --git a/modules/clightning.nix b/modules/clightning.nix index f971e07..59febba 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -5,7 +5,7 @@ with lib; let cfg = config.services.clightning; configFile = pkgs.writeText "config" '' - autolisten=false + autolisten=${toString cfg.autolisten} network=bitcoin bitcoin-rpcuser=${cfg.bitcoin-rpcuser} ''; diff --git a/modules/liquid.nix b/modules/liquid.nix new file mode 100644 index 0000000..a210da6 --- /dev/null +++ b/modules/liquid.nix @@ -0,0 +1,222 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.liquidd; + pidFile = "${cfg.dataDir}/liquidd.pid"; + configFile = pkgs.writeText "liquid.conf" '' + ${optionalString cfg.testnet "testnet=1"} + ${optionalString (cfg.dbCache != null) "dbcache=${toString cfg.dbCache}"} + ${optionalString (cfg.prune != null) "prune=${toString cfg.prune}"} + + # Connection options + ${optionalString (cfg.port != null) "port=${toString cfg.port}"} + ${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"} + listen=${if cfg.listen then "1" else "0"} + + # RPC server options + ${optionalString (cfg.rpc.port != null) "rpcport=${toString cfg.rpc.port}"} + ${concatMapStringsSep "\n" + (rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}") + (attrValues cfg.rpc.users) + } + ${optionalString (cfg.rpcuser != null) "rpcuser=${cfg.rpcuser}"} + ${optionalString (cfg.rpcpassword != null) "rpcpassword=${cfg.rpcpassword}"} + + # Extra config options (from liquidd nixos service) + ${cfg.extraConfig} + + validatepegin=0 + ''; + cmdlineOptions = concatMapStringsSep " " (arg: "'${arg}'") [ + "-datadir=${cfg.dataDir}" + "-pid=${pidFile}" + ]; + hexStr = types.strMatching "[0-9a-f]+"; + rpcUserOpts = { name, ... }: { + options = { + name = mkOption { + type = types.str; + example = "alice"; + description = '' + Username for JSON-RPC connections. + ''; + }; + passwordHMAC = mkOption { + type = with types; uniq (strMatching "[0-9a-f]+\\$[0-9a-f]{64}"); + example = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae"; + description = '' + Password HMAC-SHA-256 for JSON-RPC connections. Must be a string of the + format $. + ''; + }; + }; + config = { + name = mkDefault name; + }; + }; +in { + options = { + + services.liquidd = { + enable = mkEnableOption "Liquid daemon"; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + par=16 + rpcthreads=16 + logips=1 + + ''; + description = "Additional configurations to be appended to liquid.conf."; + }; + dataDir = mkOption { + type = types.path; + default = "/var/lib/liquidd"; + description = "The data directory for liquidd."; + }; + + user = mkOption { + type = types.str; + default = "liquid"; + description = "The user as which to run liquidd."; + }; + group = mkOption { + type = types.str; + default = cfg.user; + description = "The group as which to run liquidd."; + }; + + rpc = { + port = mkOption { + type = types.nullOr types.ints.u16; + default = null; + description = "Override the default port on which to listen for JSON-RPC connections."; + }; + users = mkOption { + default = {}; + example = { + alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae"; + bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99"; + }; + type = with types; loaOf (submodule rpcUserOpts); + description = '' + RPC user information for JSON-RPC connnections. + ''; + }; + }; + + rpcuser = mkOption { + type = types.nullOr types.string; + default = null; + description = "Username for JSON-RPC connections"; + }; + rpcpassword = mkOption { + type = types.nullOr types.string; + default = null; + description = "Password for JSON-RPC connections"; + }; + + testnet = mkOption { + type = types.bool; + default = false; + description = "Whether to use the test chain."; + }; + port = mkOption { + type = types.nullOr types.ints.u16; + default = null; + description = "Override the default port on which to listen for connections."; + }; + proxy = mkOption { + type = types.nullOr types.string; + default = null; + description = "Connect through SOCKS5 proxy"; + }; + listen = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, the liquid service will listen. + ''; + }; + dbCache = mkOption { + type = types.nullOr (types.ints.between 4 16384); + default = null; + example = 4000; + description = "Override the default database cache size in megabytes."; + }; + prune = mkOption { + type = types.nullOr (types.coercedTo + (types.enum [ "disable" "manual" ]) + (x: if x == "disable" then 0 else 1) + types.ints.unsigned + ); + default = null; + example = 10000; + description = '' + Reduce storage requirements by enabling pruning (deleting) of old + blocks. This allows the pruneblockchain RPC to be called to delete + specific blocks, and enables automatic pruning of old blocks if a + target size in MiB is provided. This mode is incompatible with -txindex + and -rescan. Warning: Reverting this setting requires re-downloading + the entire blockchain. ("disable" = disable pruning blocks, "manual" + = allow manual pruning via RPC, >=550 = automatically prune block files + to stay under the specified target size in MiB) + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.liquidd ]; + systemd.services.liquidd = { + description = "Liquid daemon"; + requires = [ "liquid-rpcpassword-key.service" ]; + after = [ "network.target" "liquid-rpcpassword-key.service" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + if ! test -e ${cfg.dataDir}; then + mkdir -m 0770 -p '${cfg.dataDir}' + chown '${cfg.user}:${cfg.group}' '${cfg.dataDir}' + fi + cp '${configFile}' '${cfg.dataDir}/liquid.conf' + chmod o-rw '${cfg.dataDir}/liquid.conf' + chown '${cfg.user}:${cfg.group}' '${cfg.dataDir}/liquid.conf' + ''; + serviceConfig = { + Type = "simple"; + User = "${cfg.user}"; + Group = "${cfg.group}"; + ExecStart = "${pkgs.liquidd}/bin/liquidd ${cmdlineOptions}"; + StateDirectory = "liquidd"; + PIDFile = "${pidFile}"; + Restart = "on-failure"; + + # Hardening measures + PrivateTmp = "true"; + ProtectSystem = "full"; + NoNewPrivileges = "true"; + PrivateDevices = "true"; + MemoryDenyWriteExecute = "true"; + + # Permission for preStart + PermissionsStartOnly = "true"; + }; + }; + users.users.${cfg.user} = { + name = cfg.user; + #uid = config.ids.uids.liquid; + group = cfg.group; + extraGroups = [ "keys" ]; + description = "Liquid daemon user"; + home = cfg.dataDir; + }; + users.groups.${cfg.group} = { + name = cfg.group; + #gid = config.ids.gids.liquid; + }; + }; +} diff --git a/modules/nixbitcoin.nix b/modules/nixbitcoin.nix index 6c87630..26255fd 100644 --- a/modules/nixbitcoin.nix +++ b/modules/nixbitcoin.nix @@ -11,6 +11,7 @@ let jq ]; allPackages = with pkgs; [ + liquidd lightning-charge.package nanopos.package nodejs-8_x @@ -26,6 +27,7 @@ in { ./lightning-charge.nix ./nanopos.nix ./nixbitcoin-webindex.nix + ./liquid.nix ]; options.services.nixbitcoin = { @@ -87,13 +89,16 @@ in { # Create user operator which can use bitcoin-cli and lightning-cli users.users.operator = { isNormalUser = true; - extraGroups = [ "clightning" config.services.bitcoind.group ]; + extraGroups = [ "clightning" config.services.bitcoind.group ] + ++ (if config.services.liquidd.enable then [ config.services.liquidd.group ] else [ ]); }; environment.interactiveShellInit = '' alias bitcoin-cli='bitcoin-cli -datadir=${config.services.bitcoind.dataDir}' alias lightning-cli='sudo -u clightning lightning-cli --lightning-dir=${config.services.clightning.dataDir}' - ''; + '' + (if config.services.liquidd.enable then '' + alias liquid-cli='liquid-cli -datadir=${config.services.liquidd.dataDir}' + '' else ""); # Unfortunately c-lightning doesn't allow setting the permissions of the rpc socket # https://github.com/ElementsProject/lightning/issues/1366 security.sudo.configFile = '' @@ -116,9 +121,11 @@ in { users.users.nanopos = {}; users.groups.nanopos = {}; + services.liquidd.enable = cfg.modules == "all"; services.lightning-charge.enable = cfg.modules == "all"; services.nanopos.enable = cfg.modules == "all"; services.nixbitcoin-webindex.enable = cfg.modules == "all"; + services.clightning.autolisten = cfg.modules == "all"; environment.systemPackages = if (cfg.modules == "all") then (minimalPackages ++ allPackages) else minimalPackages; }; } diff --git a/pkgs/liquidd.nix b/pkgs/liquidd.nix new file mode 100644 index 0000000..7844b78 --- /dev/null +++ b/pkgs/liquidd.nix @@ -0,0 +1,45 @@ +{ stdenv, fetchurl, pkgconfig, autoreconfHook, openssl, db48, boost, zeromq +, zlib, miniupnpc, qtbase ? null, qttools ? null, utillinux, protobuf, python3, qrencode, libevent +}: + +with stdenv.lib; +stdenv.mkDerivation rec{ + name = "liquid-" + version; + version = "3.14.1.21"; + + src = fetchurl { + urls = [ + "https://github.com/Blockstream/liquid/releases/download/liquid.${version}/liquid-${version}.tar.gz" + ]; + sha256 = "0fb17de2ac7169a69980f0417007263d439f6bd7045549621c1877178bac8b23"; + }; + + nativeBuildInputs = [ pkgconfig autoreconfHook ] + ++ optionals doCheck [ python3 ]; + buildInputs = [ openssl db48 boost zlib zeromq + miniupnpc protobuf libevent] + ++ optionals stdenv.isLinux [ utillinux ]; + + configureFlags = [ "--with-boost-libdir=${boost.out}/lib" + "--disable-bench" + ] ++ optionals (!doCheck) [ + "--disable-tests" + "--disable-gui-tests" + ]; + doCheck = true; + + enableParallelBuilding = true; + + meta = { + description = "An inter-exchange settlement network based on Bitcoin"; + longDescription= '' + Liquid is an inter-exchange settlement network linking together cryptocurrency exchanges and + institutions around the world, enabling faster Bitcoin transactions and the issuance of + digital assets. + ''; + homepage = http://www.github.com/blockstream/liquid; + license = licenses.mit; + # liquid needs hexdump to build, which doesn't seem to build on darwin at the moment. + platforms = platforms.linux; + }; +}