nix-bitcoin/modules/joinmarket.nix
Jonas Nick c6c14889eb
Merge #293: Module refactorings, onionServices
e2922eb4ce move rpc thread count setting to lightning modules (Erik Arvstedt)
352fc4e8fe liquid: remove insecure and redundant option 'rpcpassword' (Erik Arvstedt)
757a66b9bd liquid: move rpcuser definition to module (Erik Arvstedt)
0e00c39d47 secure-node: improve layout (Erik Arvstedt)
5f7a7962f7 backups: remove redundant option 'program' (Erik Arvstedt)
04d8560f86 secure-node: remove qrencode, tor from systemPackages (Erik Arvstedt)
323a431aba improve nodeinfo (Erik Arvstedt)
f6b883a9ac remove webindex (Erik Arvstedt)
2a240d6f4a enable-tor: disable default onion services for clightning, lnd, btcpayserver (Erik Arvstedt)
18c7842e1a modules: show warnings for obsolete options (Erik Arvstedt)
45c40c4eb9 versioning: simplify assertion evaluation (Erik Arvstedt)
bed00fe937 lnd: use onionServices for address announcing (Erik Arvstedt)
3980cd5a41 clightning: use onionServices for address announcing (Erik Arvstedt)
bd2a46cb73 spark-wallet: use onionServices (Erik Arvstedt)
87fb9f246b add 'enable-tor' preset (Erik Arvstedt)
05b5402bb1 add nix-bitcoin.onionServices (Erik Arvstedt)
fffe988248 onionAddresses: add readonly option 'dataDir' (Erik Arvstedt)
5f34b094d3 onionAddresses: improve script (Erik Arvstedt)
b266f23251 onionAddresses: use service 'script' option (Erik Arvstedt)
6d13b26d0a onionAddresses: add more precise type for option 'access' (Erik Arvstedt)
93562f76dd onionAddresses: remove redundant option 'enable' (Erik Arvstedt)
43c247e3fe onionAddresses: use StateDirectory instead of tmpfiles (Erik Arvstedt)
5c6977b006 rename onion-chef -> nix-bitcoin.onionAddresses (Erik Arvstedt)
55073eee70 remove nix-bitcoin.pkgs.lib (Erik Arvstedt)
09e0042aa8 spark-wallet: add consistent address options (Erik Arvstedt)
39f16c0b4a liquidd: add consistent address options (Erik Arvstedt)
b5d76ba1b3 electrs: add consistent address options (Erik Arvstedt)
8fa32b7f91 btcpayserver: add consistent address options (Erik Arvstedt)
e78a609687 clightning: add consistent address options (Erik Arvstedt)
b41a720c28 lnd: add consistent address options (Erik Arvstedt)
dd4a0238f9 bitcoind: group rpc options under parent option 'rpc' (Erik Arvstedt)
5b7e0d09b2 bitcoind: add consistent address options (Erik Arvstedt)

Pull request description:

ACKs for top commit:
  nixbitcoin:
    ACK e2922eb4ce
  jonasnick:
    ACK e2922eb4ce

Tree-SHA512: a85b33efe66048f06699b3997f83c9427f70f278fa66d30ee9a29c91f50723ff8bd1ffb9d968d7f08818742c8c6afb0b40dbfc14b95a4b8c3302caf9bede4198
2021-01-14 20:42:23 +00:00

254 lines
8.0 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.joinmarket;
inherit (config) nix-bitcoin-services;
nbPkgs = config.nix-bitcoin.pkgs;
secretsDir = config.nix-bitcoin.secretsDir;
inherit (config.services) bitcoind;
torAddress = builtins.head (builtins.split ":" config.services.tor.client.socksListenAddress);
# Based on https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/jmclient/jmclient/configure.py
configFile = builtins.toFile "config" ''
[DAEMON]
no_daemon = 0
daemon_port = 27183
daemon_host = localhost
use_ssl = false
[BLOCKCHAIN]
blockchain_source = bitcoin-rpc
network = ${bitcoind.network}
rpc_host = ${bitcoind.rpc.address}
rpc_port = ${toString bitcoind.rpc.port}
rpc_user = ${bitcoind.rpc.users.privileged.name}
@@RPC_PASSWORD@@
${optionalString (cfg.rpcWalletFile != null) "rpc_wallet_file=${cfg.rpcWalletFile}"}
[MESSAGING:server1]
host = darksci3bfoka7tw.onion
channel = joinmarket-pit
port = 6697
usessl = true
socks5 = true
socks5_host = ${torAddress}
socks5_port = 9050
[MESSAGING:server2]
host = ncwkrwxpq2ikcngxq3dy2xctuheniggtqeibvgofixpzvrwpa77tozqd.onion
channel = joinmarket-pit
port = 6667
usessl = false
socks5 = true
socks5_host = ${torAddress}
socks5_port = 9050
[LOGGING]
console_log_level = INFO
color = false
[POLICY]
segwit = true
native = true
merge_algorithm = default
tx_fees = 3
absurd_fee_per_kb = 350000
tx_broadcast = self
minimum_makers = 4
max_sats_freeze_reuse = -1
taker_utxo_retries = 3
taker_utxo_age = 5
taker_utxo_amtpercent = 20
accept_commitment_broadcasts = 1
commit_file_location = cmtdata/commitments.json
[PAYJOIN]
payjoin_version = 1
disable_output_substitution = 0
max_additional_fee_contribution = default
min_fee_rate = 1.1
onion_socks5_host = ${torAddress}
onion_socks5_port = 9050
tor_control_host = unix:/run/tor/control
hidden_service_ssl = false
'';
# The jm scripts create a 'logs' dir in the working dir,
# so run them inside dataDir.
cli = pkgs.runCommand "joinmarket-cli" {} ''
mkdir -p $out/bin
jm=${nbPkgs.joinmarket}/bin
cd $jm
for bin in jm-*; do
{
echo "#!${pkgs.bash}/bin/bash";
echo "cd '${cfg.dataDir}' && ${cfg.cliExec} sudo -u ${cfg.user} $jm/$bin --datadir='${cfg.dataDir}' \"\$@\"";
} > $out/bin/$bin
done
chmod -R +x $out/bin
'';
in {
options.services.joinmarket = {
enable = mkEnableOption "JoinMarket";
yieldgenerator = {
enable = mkEnableOption "yield generator bot";
customParameters = mkOption {
type = types.str;
default = "";
example = ''
txfee = 200
cjfee_a = 300
'';
description = ''
Python code to define custom yield generator parameters, as described in
https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/docs/YIELDGENERATOR.md
'';
};
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/joinmarket";
description = "The data directory for JoinMarket.";
};
user = mkOption {
type = types.str;
default = "joinmarket";
description = "The user as which to run JoinMarket.";
};
group = mkOption {
type = types.str;
default = cfg.user;
description = "The group as which to run JoinMarket.";
};
rpcWalletFile = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Name of the watch-only bitcoind wallet the JoinMarket addresses are imported to.
'';
};
cli = mkOption {
default = cli;
};
# This option is only used by netns-isolation
enforceTor = mkOption {
readOnly = true;
default = true;
};
inherit (nix-bitcoin-services) cliExec;
};
config = mkIf cfg.enable (mkMerge [{
services.bitcoind.enable = true;
environment.systemPackages = [
(hiPrio cfg.cli)
];
users.users.${cfg.user} = {
description = "joinmarket User";
group = "${cfg.group}";
home = cfg.dataDir;
extraGroups = [ "tor" ];
};
users.groups.${cfg.group} = {};
nix-bitcoin.operator = {
groups = [ cfg.group ];
sudoUsers = [ cfg.group ];
};
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
];
services.bitcoind.disablewallet = false;
# Joinmarket is TOR-only
services.tor = {
enable = true;
client.enable = true;
controlSocket.enable = true;
};
systemd.services.joinmarket = {
description = "JoinMarket Daemon";
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
path = [ pkgs.sudo ];
serviceConfig = nix-bitcoin-services.defaultHardening // {
ExecStartPre = nix-bitcoin-services.privileged ''
install -o '${cfg.user}' -g '${cfg.group}' -m 640 ${configFile} ${cfg.dataDir}/joinmarket.cfg
sed -i \
"s|@@RPC_PASSWORD@@|rpc_password = $(cat ${secretsDir}/bitcoin-rpcpassword-privileged)|" \
'${cfg.dataDir}/joinmarket.cfg'
'';
# Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet
ExecStartPost = mkIf (bitcoind.network == "mainnet") (nix-bitcoin-services.privileged ''
walletname=wallet.jmdat
pw=$(cat "${secretsDir}"/jm-wallet-password)
mnemonic=${secretsDir}/jm-wallet-seed
if [[ ! -f ${cfg.dataDir}/wallets/$walletname ]]; then
echo Create joinmarket wallet
# Use bash variables so commands don't proceed on previous failures
# (like with pipes)
cd ${cfg.dataDir} && \
out=$(sudo -u ${cfg.user} \
${nbPkgs.joinmarket}/bin/jm-genwallet \
--datadir=${cfg.dataDir} $walletname $pw)
recoveryseed=$(echo "$out" | grep 'recovery_seed')
echo "$recoveryseed" | cut -d ':' -f2 > $mnemonic
fi
'');
ExecStart = "${nbPkgs.joinmarket}/bin/joinmarketd";
WorkingDirectory = "${cfg.dataDir}"; # The service creates 'commitmentlist' in the working dir
User = "${cfg.user}";
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}";
} // nix-bitcoin-services.allowTor;
};
nix-bitcoin.secrets.jm-wallet-password.user = cfg.user;
}
(mkIf cfg.yieldgenerator.enable {
systemd.services.joinmarket-yieldgenerator = let
ygDefault = "${nbPkgs.joinmarket}/bin/jm-yg-privacyenhanced";
ygBinary = if cfg.yieldgenerator.customParameters == "" then
ygDefault
else
pkgs.runCommand "jm-yieldgenerator-custom" {
inherit (cfg.yieldgenerator) customParameters;
} ''
substitute ${ygDefault} $out \
--replace "# end of settings customization" "$customParameters"
chmod +x $out
'';
in {
description = "CoinJoin maker bot to gain privacy and passively generate income";
wantedBy = [ "joinmarket.service" ];
requires = [ "joinmarket.service" ];
after = [ "joinmarket.service" ];
preStart = let
start = ''
exec ${ygBinary} --datadir='${cfg.dataDir}' --wallet-password-stdin wallet.jmdat
'';
in ''
pw=$(cat "${secretsDir}"/jm-wallet-password)
echo "echo -n $pw | ${start}" > $RUNTIME_DIRECTORY/start
'';
serviceConfig = nix-bitcoin-services.defaultHardening // rec {
RuntimeDirectory = "joinmarket-yieldgenerator"; # Only used to create start script
RuntimeDirectoryMode = "700";
WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir
ExecStart = "${pkgs.bash}/bin/bash /run/${RuntimeDirectory}/start";
User = "${cfg.user}";
ReadWritePaths = "${cfg.dataDir}";
} // nix-bitcoin-services.allowTor;
};
})
]);
}