Merge #164: Add JoinMarket Clientserver
dd882753e6
joinmarket: add usage documentation (nixbitcoin)d0701f518c
joinmarket: automatically generate wallet (nixbitcoin)d6d3e8ff62
joinmarket: add tests (nixbitcoin)cce27da2ec
backups: add joinmarket datadir to includelist (nixbitcoin)173891fa5b
joinmarket: add module (nixbitcoin)263525d724
nix-bitcoin-services: add nb-services.privileged helper (nixbitcoin)f00d1d24c5
joinmarket: add pkg and local dependencies (nixbitcoin) Pull request description: ACKs for top commit: jonasnick: ACKdd882753e6
Tree-SHA512: ad7bf56314877045bc8dc6037f966535dc3607d9e941cd03d19b610ee789307eac07447df7f93569dfa3e7553e8fc6d9757bdf8542fb123c5a2e2adec8f907a2
This commit is contained in:
commit
4cf31f8612
@ -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"
|
||||
- |
|
||||
|
117
docs/usage.md
117
docs/usage.md
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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}
|
||||
|
@ -12,4 +12,5 @@
|
||||
spark-wallet = ./spark-wallet.nix;
|
||||
recurring-donations = ./recurring-donations.nix;
|
||||
lnd = ./lnd.nix;
|
||||
joinmarket = ./joinmarket.nix;
|
||||
}
|
||||
|
219
modules/joinmarket.nix
Normal file
219
modules/joinmarket.nix
Normal file
@ -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;
|
||||
};
|
||||
})
|
||||
]);
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
./security.nix
|
||||
./backups.nix
|
||||
./btcpayserver.nix
|
||||
./joinmarket.nix
|
||||
];
|
||||
|
||||
disabledModules = [ "services/networking/bitcoind.nix" ];
|
||||
|
@ -131,6 +131,7 @@ in {
|
||||
${ip} link del nb-br
|
||||
'';
|
||||
};
|
||||
|
||||
} //
|
||||
(let
|
||||
makeNetnsServices = n: v: let
|
||||
@ -242,6 +243,10 @@ in {
|
||||
++ optional (config.services.btcpayserver.lightningBackend == "lnd") "lnd";
|
||||
# communicates with clightning over rpc socket
|
||||
};
|
||||
joinmarket = {
|
||||
id = 25;
|
||||
connections = [ "bitcoind" ];
|
||||
};
|
||||
};
|
||||
|
||||
services.bitcoind = {
|
||||
@ -314,6 +319,9 @@ in {
|
||||
|
||||
services.nbxplorer.bind = netns.nbxplorer.address;
|
||||
services.btcpayserver.bind = netns.btcpayserver.address;
|
||||
|
||||
services.joinmarket.cliExec = mkCliExec "joinmarket";
|
||||
systemd.services.joinmarket-yieldgenerator.serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket";
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
lib: pkgs:
|
||||
|
||||
with lib;
|
||||
{
|
||||
let self = {
|
||||
# These settings roughly follow systemd's "strict" security profile
|
||||
defaultHardening = {
|
||||
PrivateTmp = "true";
|
||||
@ -56,10 +56,13 @@ with lib;
|
||||
${src}
|
||||
'';
|
||||
|
||||
# Used for ExecStart*
|
||||
privileged = src: "+${self.script src}";
|
||||
|
||||
cliExec = mkOption {
|
||||
# Used by netns-isolation to execute the cli in the service's private netns
|
||||
internal = true;
|
||||
type = types.str;
|
||||
default = "exec";
|
||||
};
|
||||
}
|
||||
}; in self
|
||||
|
@ -171,7 +171,8 @@ in {
|
||||
++ (optionals cfg.lnd.enable [ "lnd" ])
|
||||
++ (optionals cfg.liquidd.enable [ cfg.liquidd.group ])
|
||||
++ (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;
|
||||
};
|
||||
nix-bitcoin.netns-isolation.allowedUser = operatorName;
|
||||
@ -182,6 +183,9 @@ in {
|
||||
security.sudo.configFile =
|
||||
(optionalString cfg.lnd.enable ''
|
||||
${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
|
||||
|
@ -8,6 +8,7 @@ let self = {
|
||||
hwi = pkgs.callPackage ./hwi { };
|
||||
pylightning = pkgs.python3Packages.callPackage ./pylightning { };
|
||||
liquid-swap = pkgs.python3Packages.callPackage ./liquid-swap { };
|
||||
joinmarket = pkgs.callPackage ./joinmarket { };
|
||||
generate-secrets = pkgs.callPackage ./generate-secrets { };
|
||||
nixops19_09 = pkgs.callPackage ./nixops { };
|
||||
netns-exec = pkgs.callPackage ./netns-exec { };
|
||||
|
@ -18,6 +18,7 @@ makePasswordSecret liquid-rpcpassword
|
||||
makePasswordSecret lightning-charge-token
|
||||
makePasswordSecret spark-wallet-password
|
||||
makePasswordSecret backup-encryption-password
|
||||
makePasswordSecret jm-wallet-password
|
||||
|
||||
[[ -e bitcoin-HMAC-privileged ]] || makeHMAC privileged
|
||||
[[ -e bitcoin-HMAC-public ]] || makeHMAC public
|
||||
|
22
pkgs/joinmarket/bencoderpyx/default.nix
Normal file
22
pkgs/joinmarket/bencoderpyx/default.nix
Normal file
@ -0,0 +1,22 @@
|
||||
{ lib, buildPythonPackage, fetchurl, cython, pytest, coverage }:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "bencoder.pyx";
|
||||
version = "2.0.1";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://github.com/whtsky/bencoder.pyx/archive/v${version}.tar.gz";
|
||||
sha256 = "f3ff92ac706a7e4692bed5e6cbe205963327f3076f55e408eb948659923eac72";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ cython ];
|
||||
|
||||
checkInputs = [ pytest coverage ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "A fast bencode implementation in Cython";
|
||||
homepage = "https://github.com/whtsky/bencoder.pyx";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.bsd3;
|
||||
};
|
||||
}
|
24
pkgs/joinmarket/chromalog/default.nix
Normal file
24
pkgs/joinmarket/chromalog/default.nix
Normal file
@ -0,0 +1,24 @@
|
||||
{ lib, buildPythonPackage, fetchFromGitHub, colorama, future, six }:
|
||||
buildPythonPackage rec {
|
||||
pname = "chromalog";
|
||||
version = "1.0.5";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "freelan-developers";
|
||||
repo = "chromalog";
|
||||
rev = "${version}";
|
||||
sha256 = "0pj4s52rgwlvwkzrj85y92c5r9c84pz8gga45jl5spysrv41y9p0";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [ colorama future six ];
|
||||
|
||||
# enable when https://github.com/freelan-developers/chromalog/issues/6 is resolved
|
||||
doCheck = false;
|
||||
|
||||
meta = with lib; {
|
||||
description = "Enhance Python with colored logging";
|
||||
homepage = "https://github.com/freelan-developers/chromalog";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.mit;
|
||||
};
|
||||
}
|
25
pkgs/joinmarket/coincurve/default.nix
Normal file
25
pkgs/joinmarket/coincurve/default.nix
Normal file
@ -0,0 +1,25 @@
|
||||
{ lib, buildPythonPackage, fetchPypi, asn1crypto, cffi, pkg-config, libtool, libffi, requests, gmp }:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "coincurve";
|
||||
version = "13.0.0";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "1x8dpbq6bwswfyi1g4r421hnswp904l435rf7n6fj7y8q1yn51cr";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ pkg-config libtool libffi gmp ];
|
||||
|
||||
propagatedBuildInputs = [ asn1crypto cffi requests ];
|
||||
|
||||
# enable when https://github.com/ofek/coincurve/issues/47 is resolved
|
||||
doCheck = false;
|
||||
|
||||
meta = with lib; {
|
||||
description = "Cross-platform Python CFFI bindings for libsecp256k1";
|
||||
homepage = "https://github.com/ofek/coincurve";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.asl20;
|
||||
};
|
||||
}
|
71
pkgs/joinmarket/default.nix
Normal file
71
pkgs/joinmarket/default.nix
Normal file
@ -0,0 +1,71 @@
|
||||
{ stdenv, fetchurl, python3, pkgs }:
|
||||
|
||||
let
|
||||
version = "0.7.0";
|
||||
src = fetchurl {
|
||||
url = "https://github.com/JoinMarket-Org/joinmarket-clientserver/archive/v${version}.tar.gz";
|
||||
sha256 = "0ha73n3y5lykyj3pl97a619sxd2zz0lb32s5c61wm0l1h47v9l1g";
|
||||
};
|
||||
|
||||
python = python3.override {
|
||||
packageOverrides = self: super: let
|
||||
joinmarketPkg = pkg: self.callPackage pkg { inherit version src; };
|
||||
in {
|
||||
joinmarketbase = joinmarketPkg ./jmbase;
|
||||
joinmarketclient = joinmarketPkg ./jmclient;
|
||||
joinmarketbitcoin = joinmarketPkg ./jmbitcoin;
|
||||
joinmarketdaemon = joinmarketPkg ./jmdaemon;
|
||||
|
||||
chromalog = self.callPackage ./chromalog {};
|
||||
bencoderpyx = self.callPackage ./bencoderpyx {};
|
||||
coincurve = self.callPackage ./coincurve {};
|
||||
urldecode = self.callPackage ./urldecode {};
|
||||
python-bitcointx = self.callPackage ./python-bitcointx {};
|
||||
secp256k1 = self.callPackage ./secp256k1 {};
|
||||
};
|
||||
};
|
||||
|
||||
runtimePackages = with python.pkgs; [
|
||||
joinmarketbase
|
||||
joinmarketclient
|
||||
joinmarketbitcoin
|
||||
joinmarketdaemon
|
||||
];
|
||||
|
||||
genwallet = pkgs.writeScriptBin "genwallet" (builtins.readFile ./genwallet/genwallet.py);
|
||||
|
||||
pythonEnv = python.withPackages (_: runtimePackages);
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
pname = "joinmarket";
|
||||
inherit version src genwallet;
|
||||
|
||||
buildInputs = [ pythonEnv ];
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p $src-unpacked $out/bin
|
||||
tar xzf $src --strip 1 -C $src-unpacked
|
||||
|
||||
# add-utxo.py -> bin/jm-add-utxo
|
||||
cpBin() {
|
||||
cp $src-unpacked/scripts/$1 $out/bin/jm-''${1%.py}
|
||||
}
|
||||
cp $src-unpacked/scripts/joinmarketd.py $out/bin/joinmarketd
|
||||
cpBin add-utxo.py
|
||||
cpBin convert_old_wallet.py
|
||||
cpBin receive-payjoin.py
|
||||
cpBin sendpayment.py
|
||||
cpBin sendtomany.py
|
||||
cpBin tumbler.py
|
||||
cpBin wallet-tool.py
|
||||
cpBin yg-privacyenhanced.py
|
||||
cp $genwallet/bin/genwallet $out/bin/jm-genwallet
|
||||
|
||||
chmod +x -R $out/bin
|
||||
patchShebangs $out/bin
|
||||
'';
|
||||
|
||||
passthru = {
|
||||
inherit python runtimePackages pythonEnv;
|
||||
};
|
||||
}
|
36
pkgs/joinmarket/genwallet/genwallet.py
Normal file
36
pkgs/joinmarket/genwallet/genwallet.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Prototype: demonstrate you can automatically generate a wallet
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from optparse import OptionParser
|
||||
from jmclient import load_program_config, add_base_options, SegwitLegacyWallet, create_wallet, jm_single
|
||||
from jmbase.support import get_log, jmprint
|
||||
|
||||
log = get_log()
|
||||
|
||||
def main():
|
||||
parser = OptionParser(
|
||||
usage='usage: %prog [options] wallet_file_name password',
|
||||
description='Create a wallet with the given wallet name and password.')
|
||||
add_base_options(parser)
|
||||
(options, args) = parser.parse_args()
|
||||
if options.wallet_password_stdin:
|
||||
stdin = sys.stdin.read()
|
||||
password = stdin.encode("utf-8")
|
||||
else:
|
||||
assert len(args) > 1, "must provide password via stdin (see --help), or as second argument."
|
||||
password = args[1].encode("utf-8")
|
||||
load_program_config(config_path=options.datadir)
|
||||
wallet_root_path = os.path.join(jm_single().datadir, "wallets")
|
||||
wallet_name = os.path.join(wallet_root_path, args[0])
|
||||
wallet = create_wallet(wallet_name, password, 4, SegwitLegacyWallet)
|
||||
jmprint("recovery_seed:{}"
|
||||
.format(wallet.get_mnemonic_words()[0]), "important")
|
||||
wallet.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
25
pkgs/joinmarket/get-sha256.sh
Executable file
25
pkgs/joinmarket/get-sha256.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash -p git gnupg
|
||||
set -euo pipefail
|
||||
|
||||
TMPDIR="$(mktemp -d -p /tmp)"
|
||||
trap "rm -rf $TMPDIR" EXIT
|
||||
cd $TMPDIR
|
||||
|
||||
echo "Fetching latest release"
|
||||
git clone https://github.com/joinmarket-org/joinmarket-clientserver 2> /dev/null
|
||||
cd joinmarket-clientserver
|
||||
latest=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
echo "Latest release is ${latest}"
|
||||
|
||||
# GPG verification
|
||||
export GNUPGHOME=$TMPDIR
|
||||
echo "Fetching Adam Gibson's key"
|
||||
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 2B6FC204D9BF332D062B461A141001A1AF77F20B 2> /dev/null
|
||||
echo "Verifying latest release"
|
||||
git verify-tag ${latest}
|
||||
|
||||
echo "tag: ${latest}"
|
||||
# The prefix option is necessary because GitHub prefixes the archive contents in this format
|
||||
echo "sha256: $(nix-hash --type sha256 --flat --base32 \
|
||||
<(git archive --format tar.gz --prefix=joinmarket-clientserver-"${latest//v}"/ ${latest}))"
|
16
pkgs/joinmarket/jmbase/default.nix
Normal file
16
pkgs/joinmarket/jmbase/default.nix
Normal file
@ -0,0 +1,16 @@
|
||||
{ version, src, lib, buildPythonPackage, fetchurl, future, twisted, service-identity, chromalog }:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "joinmarketbase";
|
||||
inherit version src;
|
||||
|
||||
postUnpack = "sourceRoot=$sourceRoot/jmbase";
|
||||
|
||||
propagatedBuildInputs = [ future twisted service-identity chromalog ];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/Joinmarket-Org/joinmarket-clientserver";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.gpl3;
|
||||
};
|
||||
}
|
18
pkgs/joinmarket/jmbitcoin/default.nix
Normal file
18
pkgs/joinmarket/jmbitcoin/default.nix
Normal file
@ -0,0 +1,18 @@
|
||||
{ version, src, lib, buildPythonPackage, fetchurl, future, coincurve, urldecode, pyaes, python-bitcointx, secp256k1, joinmarketbase }:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "joinmarketbitcoin";
|
||||
inherit version src;
|
||||
|
||||
postUnpack = "sourceRoot=$sourceRoot/jmbitcoin";
|
||||
|
||||
propagatedBuildInputs = [ future coincurve urldecode pyaes python-bitcointx secp256k1 ];
|
||||
|
||||
checkInputs = [ joinmarketbase ];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/Joinmarket-Org/joinmarket-clientserver";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.gpl3;
|
||||
};
|
||||
}
|
20
pkgs/joinmarket/jmclient/default.nix
Normal file
20
pkgs/joinmarket/jmclient/default.nix
Normal file
@ -0,0 +1,20 @@
|
||||
{ version, src, lib, buildPythonPackage, fetchurl, future, configparser, joinmarketbase, mnemonic, argon2_cffi, bencoderpyx, pyaes, joinmarketbitcoin, txtorcon }:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "joinmarketclient";
|
||||
inherit version src;
|
||||
|
||||
postUnpack = "sourceRoot=$sourceRoot/jmclient";
|
||||
|
||||
checkInputs = [ joinmarketbitcoin txtorcon ];
|
||||
|
||||
# configparser may need to be compiled with python_version<"3.2"
|
||||
propagatedBuildInputs = [ future configparser joinmarketbase mnemonic argon2_cffi bencoderpyx pyaes ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Client library for Bitcoin coinjoins";
|
||||
homepage = "https://github.com/Joinmarket-Org/joinmarket-clientserver";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.gpl3;
|
||||
};
|
||||
}
|
17
pkgs/joinmarket/jmdaemon/default.nix
Normal file
17
pkgs/joinmarket/jmdaemon/default.nix
Normal file
@ -0,0 +1,17 @@
|
||||
{ version, src, lib, buildPythonPackage, fetchurl, future, txtorcon, pyopenssl, libnacl, joinmarketbase }:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "joinmarketdaemon";
|
||||
inherit version src;
|
||||
|
||||
postUnpack = "sourceRoot=$sourceRoot/jmdaemon";
|
||||
|
||||
propagatedBuildInputs = [ future txtorcon pyopenssl libnacl joinmarketbase ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Client library for Bitcoin coinjoins";
|
||||
homepage = "https://github.com/Joinmarket-Org/joinmarket-clientserver";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.gpl3;
|
||||
};
|
||||
}
|
27
pkgs/joinmarket/python-bitcointx/default.nix
Normal file
27
pkgs/joinmarket/python-bitcointx/default.nix
Normal file
@ -0,0 +1,27 @@
|
||||
{ lib, buildPythonPackage, fetchurl, secp256k1, openssl }:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "python-bitcointx";
|
||||
version = "1.1.1";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://github.com/Simplexum/${pname}/archive/${pname}-v${version}.tar.gz";
|
||||
sha256 = "35edd694473517508367338888633954eaa91b2622b3caada8fd3030ddcacba2";
|
||||
};
|
||||
|
||||
patchPhase = ''
|
||||
for path in core/secp256k1.py tests/test_load_secp256k1.py; do
|
||||
substituteInPlace "bitcointx/$path" \
|
||||
--replace "ctypes.util.find_library('secp256k1')" "'${secp256k1}/lib/libsecp256k1.so'"
|
||||
done
|
||||
substituteInPlace bitcointx/core/key.py \
|
||||
--replace "ctypes.util.find_library('ssl')" "'${openssl.out}/lib/libssl.so'"
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
||||
description = "Interface to Bitcoin transaction data structures";
|
||||
homepage = "https://github.com/Simplexum/python-bitcointx";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
license = licenses.gpl3;
|
||||
};
|
||||
}
|
24
pkgs/joinmarket/python-bitcointx/get-sha256.sh
Executable file
24
pkgs/joinmarket/python-bitcointx/get-sha256.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash -p git gnupg
|
||||
set -euo pipefail
|
||||
|
||||
TMPDIR="$(mktemp -d -p /tmp)"
|
||||
trap "rm -rf $TMPDIR" EXIT
|
||||
cd $TMPDIR
|
||||
|
||||
echo "Fetching latest release"
|
||||
git clone https://github.com/simplexum/python-bitcointx 2> /dev/null
|
||||
cd python-bitcointx
|
||||
latest=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
echo "Latest release is ${latest}"
|
||||
|
||||
# GPG verification
|
||||
export GNUPGHOME=$TMPDIR
|
||||
echo "Fetching Dimitry Pethukov's Key"
|
||||
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys B17A35BBA187395784E2A6B32301D26BDC15160D 2> /dev/null
|
||||
echo "Verifying latest release"
|
||||
git verify-commit ${latest}
|
||||
|
||||
echo "tag: ${latest}"
|
||||
# The prefix option is necessary because GitHub prefixes the archive contents in this format
|
||||
echo "sha256: $(git archive --format tar.gz --prefix=python-bitcointx-"${latest}"/ ${latest} | sha256sum | cut -d\ -f1)"
|
28
pkgs/joinmarket/secp256k1/default.nix
Normal file
28
pkgs/joinmarket/secp256k1/default.nix
Normal file
@ -0,0 +1,28 @@
|
||||
{ stdenv, fetchFromGitHub, autoreconfHook }:
|
||||
|
||||
let inherit (stdenv.lib) optionals; in
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "secp256k1";
|
||||
|
||||
version = "2019-10-11";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "bitcoin-core";
|
||||
repo = "secp256k1";
|
||||
rev = "0d9540b13ffcd7cd44cc361b8744b93d88aa76ba";
|
||||
sha256 = "05zwhv8ffzrfdzqbsb4zm4kjdbjxqy5jh9r83fic0qpk2mkvc2i2";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ autoreconfHook ];
|
||||
|
||||
configureFlags = ["--enable-module-recovery" "--disable-jni" "--enable-experimental" "--enable-module-ecdh" "--enable-benchmark=no" ];
|
||||
|
||||
meta = with stdenv.lib; {
|
||||
description = "Optimized C library for EC operations on curve secp256k1";
|
||||
homepage = "https://github.com/bitcoin-core/secp256k1";
|
||||
license = with licenses; [ mit ];
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
platforms = with platforms; unix;
|
||||
};
|
||||
}
|
16
pkgs/joinmarket/urldecode/default.nix
Normal file
16
pkgs/joinmarket/urldecode/default.nix
Normal file
@ -0,0 +1,16 @@
|
||||
{ lib, buildPythonPackage, fetchPypi }:
|
||||
buildPythonPackage rec {
|
||||
pname = "urldecode";
|
||||
version = "0.1";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "0w8my7kdwxppsfzzi1b2cxhypm6r1fsrnb2hnd752axq4gfsddjj";
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
description = "A simple function to decode an encoded url";
|
||||
homepage = "https://github.com/jennyq/urldecode";
|
||||
maintainers = with maintainers; [ nixbitcoin ];
|
||||
};
|
||||
}
|
@ -13,7 +13,8 @@ static char *allowed_netns[] = {
|
||||
"nb-lnd",
|
||||
"nb-lightning-loop",
|
||||
"nb-bitcoind",
|
||||
"nb-liquidd"
|
||||
"nb-liquidd",
|
||||
"nb-joinmarket"
|
||||
};
|
||||
|
||||
int is_netns_allowed(char *netns) {
|
||||
|
12
test/base.py
12
test/base.py
@ -102,6 +102,14 @@ def run_tests(extra_tests):
|
||||
|
||||
assert_running("onion-chef")
|
||||
|
||||
assert_running("joinmarket")
|
||||
machine.wait_until_succeeds(
|
||||
log_has_string("joinmarket", "P2EPDaemonServerProtocolFactory starting on 27184")
|
||||
)
|
||||
machine.wait_until_succeeds(
|
||||
log_has_string("joinmarket-yieldgenerator", "Failure to get blockheight",)
|
||||
)
|
||||
|
||||
# FIXME: use 'wait_for_unit' because 'create-web-index' always fails during startup due
|
||||
# to incomplete unit dependencies.
|
||||
# 'create-web-index' implicitly tests 'nodeinfo'.
|
||||
@ -150,6 +158,10 @@ def run_tests(extra_tests):
|
||||
"export $(cat /secrets/backup-encryption-env); duplicity list-current-files 'file:///var/lib/localBackups'",
|
||||
"secrets/lnd-seed-mnemonic",
|
||||
)
|
||||
assert_matches(
|
||||
"export $(cat /secrets/backup-encryption-env); duplicity list-current-files 'file:///var/lib/localBackups'",
|
||||
"secrets/jm-wallet-seed",
|
||||
)
|
||||
assert_matches(
|
||||
"export $(cat /secrets/backup-encryption-env); duplicity list-current-files 'file:///var/lib/localBackups'",
|
||||
"var/lib/bitcoind/wallet.dat",
|
||||
|
@ -57,6 +57,15 @@ import ./make-test.nix rec {
|
||||
# needed to test macaroon creation
|
||||
environment.systemPackages = with pkgs; [ openssl xxd ];
|
||||
|
||||
services.joinmarket.enable = true;
|
||||
services.joinmarket.yieldgenerator = {
|
||||
enable = true;
|
||||
customParameters = ''
|
||||
txfee = 200
|
||||
cjfee_a = 300
|
||||
'';
|
||||
};
|
||||
|
||||
# to test that unused secrets are made inaccessible by 'setup-secrets'
|
||||
systemd.services.generate-secrets.postStart = ''
|
||||
install -o nobody -g nogroup -m777 <(:) /secrets/dummy
|
||||
|
Loading…
Reference in New Issue
Block a user