Skip to content

Commit

Permalink
Merge #164: Add JoinMarket Clientserver
Browse files Browse the repository at this point in the history
dd88275 joinmarket: add usage documentation (nixbitcoin)
d0701f5 joinmarket: automatically generate wallet (nixbitcoin)
d6d3e8f joinmarket: add tests (nixbitcoin)
cce27da backups: add joinmarket datadir to includelist (nixbitcoin)
173891f joinmarket: add module (nixbitcoin)
263525d nix-bitcoin-services: add nb-services.privileged helper (nixbitcoin)
f00d1d2 joinmarket: add pkg and local dependencies (nixbitcoin)

Pull request description:

ACKs for top commit:
  jonasnick:
    ACK dd88275

Tree-SHA512: ad7bf56314877045bc8dc6037f966535dc3607d9e941cd03d19b610ee789307eac07447df7f93569dfa3e7553e8fc6d9757bdf8542fb123c5a2e2adec8f907a2
  • Loading branch information
jonasnick committed Sep 22, 2020
2 parents bde530c + dd88275 commit 4cf31f8
Show file tree
Hide file tree
Showing 29 changed files with 764 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ env:
- PKG=liquid-swap STABLE=1
- PKG=lightning-loop STABLE=0
- PKG=nixops19_09 STABLE=1
- PKG=joinmarket STABLE=1
- PKG=joinmarket STABLE=0
script:
- printf '%s (%s)\n' "$NIX_PATH" "$VER"
- |
Expand Down
117 changes: 117 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,120 @@ Initialize a Trezor for Bitcoin Core's Hardware Wallet Interface
```
8. Follow Bitcoin Core's instructions on [Using Bitcoin Core with Hardware Wallets](https://github.com/bitcoin-core/HWI/blob/master/docs/bitcoin-core-usage.md) to use your Trezor with `bitcoin-cli` on your nix-bitcoin node
JoinMarket
---
## Diff to regular JoinMarket usage
For clarity reasons, nix-bitcoin renames all scripts to `jm-*` without `.py`, for
example `wallet-tool.py` becomes `jm-wallet-tool`. The rest of this section
details nix-bitcoin specific workflows for JoinMarket.
## Initialize JoinMarket Wallet
By default, nix-bitcoin's JoinMarket module automatically generates a wallet for
you. If however, you want to manually initialize your wallet, follow these steps.
1. Enable JoinMarket in your node configuration
```
services.joinmarket.enable = true;
```
2. Move the automatically generated `wallet.jmdat`
```console
rm /var/lib/joinmarket/wallet.jmdat /var/lib/joinmarket/bak.jmdat
```
3. Generate wallet on your node
```console
jm-wallet-tool generate
```
Follow the on-screen instructions and write down your seed.
In order to use nix-bitcoin's `joinmarket.yieldgenerator`, use the password
from `/secrets/jm-wallet-password` and use the suggested default wallet name
`wallet.jmdat`. If you want to use your own `jm-wallet-password`, simply
replace the password string in your local secrets directory.
## Run the tumbler
The tumbler needs to be able to run in the background for a long time, use screen
to run it accross ssh sessions. You can also use tmux in the same fashion.
1. Add screen to your `environment.systemPackages`, for example
```
environment.systemPackages = with pkgs; [
vim
screen
];
```
2. Start the screen session
```console
screen -S "tumbler"
```
2. Start the tumbler
Example: Tumbling into your wallet after buying from an exchange to improve privacy:
```console
jm-tumbler wallet.jmdat <addr1> <addr2> <addr3>
```
After tumbling your bitcoin end up in these three addresses. You can now
spend them without the exchange collecting data on your purchases.
Get more information [here](https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/docs/tumblerguide.md)
3. Detach the screen session to leave the tumbler running in the background
```
Ctrl-a d or Ctrl-a Ctrl-d
```
4. Re-attach to the screen session
```console
screen -r tumbler
```
5. End screen session
Type exit when tumbler is done
```console
exit
```
## Run a "maker" or "yield generator"
The maker/yield generator in nix-bitcoin is implemented using a systemd service.
See [here](https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/docs/YIELDGENERATOR.md) for more yield generator information.
1. Enable yield generator bot in your node configuration
```
services.joinmarket.yieldgenerator.enable = true;
# Optional: Add custom parameters
services.joinmarket.yieldgenerator.customParameters = ''
txfee = 200
cjfee_a = 300
'';
```
2. Check service status
```console
systemctl status joinmarket-yieldgenerator
```
3. Profit
10 changes: 10 additions & 0 deletions examples/configuration.nix
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@
# and electrs data directory, enable
# 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.
networking.hostName = "nix-bitcoin";
time.timeZone = "UTC";
Expand Down
2 changes: 2 additions & 0 deletions modules/backups.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ let
${config.services.lightning-charge.dataDir}
${config.services.nbxplorer.dataDir}
${config.services.btcpayserver.dataDir}
${config.services.joinmarket.dataDir}
/secrets/jm-wallet-seed
/var/lib/tor
# Extra files
${cfg.extraFiles}
Expand Down
1 change: 1 addition & 0 deletions modules/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
spark-wallet = ./spark-wallet.nix;
recurring-donations = ./recurring-donations.nix;
lnd = ./lnd.nix;
joinmarket = ./joinmarket.nix;
}
219 changes: 219 additions & 0 deletions modules/joinmarket.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
{ 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" ];
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'
'';
ExecStartPost = 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} \
${pkgs.nix-bitcoin.joinmarket}/bin/jm-genwallet \
--datadir=${cfg.dataDir} $walletname $pw)
recoveryseed=$(echo "$out" | grep 'recovery_seed')
echo "$recoveryseed" | cut -d ':' -f2 > $mnemonic
fi
'';
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;
};
})
]);
}
1 change: 1 addition & 0 deletions modules/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
./security.nix
./backups.nix
./btcpayserver.nix
./joinmarket.nix
];

disabledModules = [ "services/networking/bitcoind.nix" ];
Expand Down
Loading

0 comments on commit 4cf31f8

Please sign in to comment.