joinmarket: add module

This commit is contained in:
nixbitcoin 2020-04-23 18:18:47 +02:00
parent 263525d724
commit 173891fa5b
No known key found for this signature in database
GPG Key ID: DD11F9AD5308B3BA
8 changed files with 230 additions and 2 deletions

View File

@ -172,6 +172,16 @@
# and electrs data directory, enable # and electrs data directory, enable
# services.backups.with-bulk-data = true; # services.backups.with-bulk-data = true;
### JOINMARKET
# Enable this module to allow using JoinMarket's user interactive scripts (including
# tumbler.py).
# Note: JoinMarket has full access to bitcoind, including its wallet functionality.
# services.joinmarket.enable = true;
# Enable this option to enable the JoinMarket Yield Generator Bot. You will be able to
# earn sats by providing CoinJoin liquidity. This makes it impossible to use other
# scripts that access your wallet.
# services.joinmarket.yieldgenerator.enable = true;
# FIXME: Define your hostname. # FIXME: Define your hostname.
networking.hostName = "nix-bitcoin"; networking.hostName = "nix-bitcoin";
time.timeZone = "UTC"; time.timeZone = "UTC";

View File

@ -12,4 +12,5 @@
spark-wallet = ./spark-wallet.nix; spark-wallet = ./spark-wallet.nix;
recurring-donations = ./recurring-donations.nix; recurring-donations = ./recurring-donations.nix;
lnd = ./lnd.nix; lnd = ./lnd.nix;
joinmarket = ./joinmarket.nix;
} }

202
modules/joinmarket.nix Normal file
View File

@ -0,0 +1,202 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.joinmarket;
inherit (config) nix-bitcoin-services;
secretsDir = config.nix-bitcoin.secretsDir;
torAddress = builtins.head (builtins.split ":" config.services.tor.client.socksListenAddress);
configFile = builtins.toFile "config" ''
# Based on https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/jmclient/jmclient/configure.py
[DAEMON]
no_daemon = 0
daemon_port = 27183
daemon_host = localhost
use_ssl = false
[BLOCKCHAIN]
blockchain_source = bitcoin-rpc
network = mainnet
rpc_host = ${builtins.elemAt config.services.bitcoind.rpcbind 0}
rpc_port = 8332
rpc_user = ${config.services.bitcoind.rpc.users.privileged.name}
@@RPC_PASSWORD@@
[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 = false
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
'';
# 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=${pkgs.nix-bitcoin.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.";
};
cli = mkOption {
default = cli;
};
inherit (nix-bitcoin-services) cliExec;
};
config = mkIf cfg.enable (mkMerge [{
environment.systemPackages = [
(hiPrio cfg.cli)
];
users.users.${cfg.user} = {
description = "joinmarket User";
group = "${cfg.group}";
home = cfg.dataDir;
};
users.groups.${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;
};
systemd.services.joinmarket = {
description = "JoinMarket Daemon";
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
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 ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-privileged)|" \
'${cfg.dataDir}/joinmarket.cfg'
'';
ExecStart = "${pkgs.nix-bitcoin.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;
};
}
(mkIf cfg.yieldgenerator.enable {
nix-bitcoin.secrets.jm-wallet-password.user = cfg.user;
systemd.services.joinmarket-yieldgenerator = let
ygDefault = "${pkgs.nix-bitcoin.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;
};
})
]);
}

View File

@ -19,6 +19,7 @@
./security.nix ./security.nix
./backups.nix ./backups.nix
./btcpayserver.nix ./btcpayserver.nix
./joinmarket.nix
]; ];
disabledModules = [ "services/networking/bitcoind.nix" ]; disabledModules = [ "services/networking/bitcoind.nix" ];

View File

@ -131,6 +131,7 @@ in {
${ip} link del nb-br ${ip} link del nb-br
''; '';
}; };
} // } //
(let (let
makeNetnsServices = n: v: let makeNetnsServices = n: v: let
@ -242,6 +243,10 @@ in {
++ optional (config.services.btcpayserver.lightningBackend == "lnd") "lnd"; ++ optional (config.services.btcpayserver.lightningBackend == "lnd") "lnd";
# communicates with clightning over rpc socket # communicates with clightning over rpc socket
}; };
joinmarket = {
id = 25;
connections = [ "bitcoind" ];
};
}; };
services.bitcoind = { services.bitcoind = {
@ -314,6 +319,9 @@ in {
services.nbxplorer.bind = netns.nbxplorer.address; services.nbxplorer.bind = netns.nbxplorer.address;
services.btcpayserver.bind = netns.btcpayserver.address; services.btcpayserver.bind = netns.btcpayserver.address;
services.joinmarket.cliExec = mkCliExec "joinmarket";
systemd.services.joinmarket-yieldgenerator.serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket";
} }
]); ]);
} }

View File

@ -171,7 +171,8 @@ in {
++ (optionals cfg.lnd.enable [ "lnd" ]) ++ (optionals cfg.lnd.enable [ "lnd" ])
++ (optionals cfg.liquidd.enable [ cfg.liquidd.group ]) ++ (optionals cfg.liquidd.enable [ cfg.liquidd.group ])
++ (optionals (cfg.hardware-wallets.ledger || cfg.hardware-wallets.trezor) ++ (optionals (cfg.hardware-wallets.ledger || cfg.hardware-wallets.trezor)
[ cfg.hardware-wallets.group ]); [ cfg.hardware-wallets.group ])
++ (optionals cfg.joinmarket.enable [ cfg.joinmarket.group ]);
openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys; openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys;
}; };
nix-bitcoin.netns-isolation.allowedUser = operatorName; nix-bitcoin.netns-isolation.allowedUser = operatorName;
@ -182,6 +183,9 @@ in {
security.sudo.configFile = security.sudo.configFile =
(optionalString cfg.lnd.enable '' (optionalString cfg.lnd.enable ''
${operatorName} ALL=(lnd) NOPASSWD: ALL ${operatorName} ALL=(lnd) NOPASSWD: ALL
'') +
(optionalString cfg.joinmarket.enable ''
${operatorName} ALL=(${cfg.joinmarket.user}) NOPASSWD: ALL
''); '');
# Enable nixops ssh for operator (`nixops ssh operator@mynode`) on nixops-vbox deployments # Enable nixops ssh for operator (`nixops ssh operator@mynode`) on nixops-vbox deployments

View File

@ -18,6 +18,7 @@ makePasswordSecret liquid-rpcpassword
makePasswordSecret lightning-charge-token makePasswordSecret lightning-charge-token
makePasswordSecret spark-wallet-password makePasswordSecret spark-wallet-password
makePasswordSecret backup-encryption-password makePasswordSecret backup-encryption-password
touch jm-wallet-password
[[ -e bitcoin-HMAC-privileged ]] || makeHMAC privileged [[ -e bitcoin-HMAC-privileged ]] || makeHMAC privileged
[[ -e bitcoin-HMAC-public ]] || makeHMAC public [[ -e bitcoin-HMAC-public ]] || makeHMAC public

View File

@ -13,7 +13,8 @@ static char *allowed_netns[] = {
"nb-lnd", "nb-lnd",
"nb-lightning-loop", "nb-lightning-loop",
"nb-bitcoind", "nb-bitcoind",
"nb-liquidd" "nb-liquidd",
"nb-joinmarket"
}; };
int is_netns_allowed(char *netns) { int is_netns_allowed(char *netns) {