bbebd0b383
NixOS will soon use CommonMark Markdown for option descriptions. We can then switch back the old, slightly clearer bracket syntax.
293 lines
9.8 KiB
Nix
293 lines
9.8 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
|
|
with lib;
|
|
let
|
|
options = {
|
|
services.liquidd = {
|
|
enable = mkEnableOption "Liquid sidechain";
|
|
address = mkOption {
|
|
type = types.str;
|
|
default = "127.0.0.1";
|
|
description = "Address to listen for peer connections.";
|
|
};
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 7042;
|
|
description = "Override the default port on which to listen for connections.";
|
|
};
|
|
onionPort = mkOption {
|
|
type = types.nullOr types.port;
|
|
# When the liquidd onion service is enabled, add an onion-tagged socket
|
|
# to distinguish local connections from Tor connections
|
|
default = if (config.nix-bitcoin.onionServices.liquidd.enable or false) then 7043 else null;
|
|
description = ''
|
|
Port to listen for Tor peer connections.
|
|
If set, inbound connections to this port are tagged as onion peers.
|
|
'';
|
|
};
|
|
listen = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Listen for peer connections at `address:port`
|
|
and `address:onionPort` (if `onionPort` is set).
|
|
'';
|
|
};
|
|
listenWhitelisted = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Listen for peer connections at `address:whitelistedPort`.
|
|
Peers connected through this socket are automatically whitelisted.
|
|
'';
|
|
};
|
|
whitelistedPort = mkOption {
|
|
type = types.port;
|
|
default = 7044;
|
|
description = "See `listenWhitelisted`.";
|
|
};
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
example = ''
|
|
par=16
|
|
rpcthreads=16
|
|
logips=1
|
|
'';
|
|
description = "Extra lines appended to <filename>elements.conf</filename>.";
|
|
};
|
|
dataDir = mkOption {
|
|
type = types.path;
|
|
default = "/var/lib/liquidd";
|
|
description = "The data directory for liquidd.";
|
|
};
|
|
rpc = {
|
|
address = mkOption {
|
|
type = types.str;
|
|
default = "127.0.0.1";
|
|
description = "Address to listen for JSON-RPC connections.";
|
|
};
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 7041;
|
|
description = "Port to listen for JSON-RPC connections.";
|
|
};
|
|
users = mkOption {
|
|
default = {};
|
|
example = {
|
|
alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
|
|
bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
|
|
};
|
|
type = with types; attrsOf (submodule rpcUserOpts);
|
|
description = ''
|
|
RPC user information for JSON-RPC connections.
|
|
'';
|
|
};
|
|
};
|
|
rpcallowip = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ "127.0.0.1" ];
|
|
description = ''
|
|
Allow JSON-RPC connections from specified source.
|
|
'';
|
|
};
|
|
rpcuser = mkOption {
|
|
type = types.str;
|
|
default = "liquidrpc";
|
|
description = "Username for JSON-RPC connections";
|
|
};
|
|
proxy = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null;
|
|
description = "Connect through SOCKS5 proxy";
|
|
};
|
|
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)
|
|
'';
|
|
};
|
|
validatepegin = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
description = ''
|
|
Validate pegin claims. All functionaries must run this.
|
|
'';
|
|
};
|
|
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.";
|
|
};
|
|
cli = mkOption {
|
|
readOnly = true;
|
|
default = pkgs.writeScriptBin "elements-cli" ''
|
|
${nbPkgs.elementsd}/bin/elements-cli -datadir='${cfg.dataDir}' "$@"
|
|
'';
|
|
defaultText = "(See source)";
|
|
description = "Binary to connect with the liquidd instance.";
|
|
};
|
|
swapCli = mkOption {
|
|
default = pkgs.writeScriptBin "liquidswap-cli" ''
|
|
${nbPkgs.liquid-swap}/bin/liquidswap-cli -c '${cfg.dataDir}/elements.conf' "$@"
|
|
'';
|
|
defaultText = "(See source)";
|
|
description = "Binary for managing liquid swaps.";
|
|
};
|
|
tor = nbLib.tor;
|
|
};
|
|
};
|
|
|
|
cfg = config.services.liquidd;
|
|
nbLib = config.nix-bitcoin.lib;
|
|
nbPkgs = config.nix-bitcoin.pkgs;
|
|
secretsDir = config.nix-bitcoin.secretsDir;
|
|
|
|
bitcoind = config.services.bitcoind;
|
|
|
|
configFile = pkgs.writeText "elements.conf" ''
|
|
# We're already logging via journald
|
|
nodebuglogfile=1
|
|
|
|
startupnotify=/run/current-system/systemd/bin/systemd-notify --ready
|
|
|
|
chain=${bitcoind.makeNetworkName "liquidv1" ''
|
|
regtest
|
|
[regtest]'' # Add [regtest] config section
|
|
}
|
|
${optionalString (cfg.dbCache != null) "dbcache=${toString cfg.dbCache}"}
|
|
${optionalString (cfg.prune != null) "prune=${toString cfg.prune}"}
|
|
${optionalString (cfg.validatepegin != null) "validatepegin=${if cfg.validatepegin then "1" else "0"}"}
|
|
|
|
# Connection options
|
|
listen=${if (cfg.listen || cfg.listenWhitelisted) then "1" else "0"}
|
|
${optionalString cfg.listen
|
|
"bind=${cfg.address}:${toString cfg.port}"}
|
|
${optionalString (cfg.listen && cfg.onionPort != null)
|
|
"bind=${cfg.address}:${toString cfg.onionPort}=onion"}
|
|
${optionalString cfg.listenWhitelisted
|
|
"whitebind=${cfg.address}:${toString cfg.whitelistedPort}"}
|
|
${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"}
|
|
|
|
# RPC server options
|
|
rpcport=${toString cfg.rpc.port}
|
|
${concatMapStringsSep "\n"
|
|
(rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
|
|
(attrValues cfg.rpc.users)
|
|
}
|
|
rpcbind=${cfg.rpc.address}
|
|
rpcconnect=${cfg.rpc.address}
|
|
${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip}
|
|
rpcuser=${cfg.rpcuser}
|
|
mainchainrpchost=${nbLib.address bitcoind.rpc.address}
|
|
mainchainrpcport=${toString bitcoind.rpc.port}
|
|
mainchainrpcuser=${bitcoind.rpc.users.public.name}
|
|
|
|
# Extra config options (from liquidd nixos service)
|
|
${cfg.extraConfig}
|
|
'';
|
|
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 `salt-hex$hmac-hex`.
|
|
'';
|
|
};
|
|
};
|
|
config = {
|
|
name = mkDefault name;
|
|
};
|
|
};
|
|
in {
|
|
inherit options;
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = [
|
|
{ assertion = bitcoind.regtest -> cfg.validatepegin != true;
|
|
message = "liquidd: `validatepegin` is incompatible with regtest.";
|
|
}
|
|
];
|
|
|
|
services.bitcoind.enable = true;
|
|
|
|
environment.systemPackages = [
|
|
nbPkgs.elementsd
|
|
(hiPrio cfg.cli)
|
|
(hiPrio cfg.swapCli)
|
|
];
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
|
|
];
|
|
|
|
systemd.services.liquidd = {
|
|
requires = [ "bitcoind.service" ];
|
|
after = [ "bitcoind.service" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
preStart = ''
|
|
install -m 640 ${configFile} '${cfg.dataDir}/elements.conf'
|
|
{
|
|
echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)"
|
|
echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword-public)"
|
|
} >> '${cfg.dataDir}/elements.conf'
|
|
'';
|
|
serviceConfig = nbLib.defaultHardening // {
|
|
Type = "notify";
|
|
NotifyAccess = "all";
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
ExecStart = "${nbPkgs.elementsd}/bin/elementsd -datadir='${cfg.dataDir}'";
|
|
Restart = "on-failure";
|
|
ReadWritePaths = cfg.dataDir;
|
|
} // nbLib.allowedIPAddresses cfg.tor.enforce;
|
|
};
|
|
|
|
users.users.${cfg.user} = {
|
|
isSystemUser = true;
|
|
group = cfg.group;
|
|
extraGroups = [ "bitcoinrpc-public" ];
|
|
};
|
|
users.groups.${cfg.group} = {};
|
|
nix-bitcoin.operator.groups = [ cfg.group ];
|
|
|
|
nix-bitcoin.secrets.liquid-rpcpassword.user = cfg.user;
|
|
nix-bitcoin.generateSecretsCmds.liquid = ''
|
|
makePasswordSecret liquid-rpcpassword
|
|
'';
|
|
};
|
|
}
|