diff --git a/modules/backups.nix b/modules/backups.nix index cb7b5e2..4915067 100644 --- a/modules/backups.nix +++ b/modules/backups.nix @@ -19,6 +19,7 @@ let ${config.services.nbxplorer.dataDir} ${config.services.btcpayserver.dataDir} ${config.services.joinmarket.dataDir} + /secrets/jm-wallet-seed /var/lib/tor # Extra files ${cfg.extraFiles} diff --git a/modules/joinmarket.nix b/modules/joinmarket.nix index 80c2593..7ce8b53 100644 --- a/modules/joinmarket.nix +++ b/modules/joinmarket.nix @@ -143,13 +143,30 @@ in { 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 ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-privileged)|" \ + "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}"; diff --git a/pkgs/generate-secrets/generate-secrets.sh b/pkgs/generate-secrets/generate-secrets.sh index 06b3f69..831b235 100755 --- a/pkgs/generate-secrets/generate-secrets.sh +++ b/pkgs/generate-secrets/generate-secrets.sh @@ -18,7 +18,7 @@ makePasswordSecret liquid-rpcpassword makePasswordSecret lightning-charge-token makePasswordSecret spark-wallet-password makePasswordSecret backup-encryption-password -touch jm-wallet-password +makePasswordSecret jm-wallet-password [[ -e bitcoin-HMAC-privileged ]] || makeHMAC privileged [[ -e bitcoin-HMAC-public ]] || makeHMAC public diff --git a/pkgs/joinmarket/default.nix b/pkgs/joinmarket/default.nix index ea8f04e..fcaaedb 100644 --- a/pkgs/joinmarket/default.nix +++ b/pkgs/joinmarket/default.nix @@ -1,4 +1,4 @@ -{ stdenv, fetchurl, python3 }: +{ stdenv, fetchurl, python3, pkgs }: let version = "0.7.0"; @@ -32,11 +32,13 @@ let joinmarketdaemon ]; + genwallet = pkgs.writeScriptBin "genwallet" (builtins.readFile ./genwallet/genwallet.py); + pythonEnv = python.withPackages (_: runtimePackages); in stdenv.mkDerivation { pname = "joinmarket"; - inherit version src; + inherit version src genwallet; buildInputs = [ pythonEnv ]; @@ -57,6 +59,7 @@ stdenv.mkDerivation { 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 diff --git a/pkgs/joinmarket/genwallet/genwallet.py b/pkgs/joinmarket/genwallet/genwallet.py new file mode 100644 index 0000000..51de87f --- /dev/null +++ b/pkgs/joinmarket/genwallet/genwallet.py @@ -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() diff --git a/test/base.py b/test/base.py index 97b2571..b7db2ae 100644 --- a/test/base.py +++ b/test/base.py @@ -107,7 +107,7 @@ def run_tests(extra_tests): log_has_string("joinmarket", "P2EPDaemonServerProtocolFactory starting on 27184") ) machine.wait_until_succeeds( - log_has_string("joinmarket-yieldgenerator", "Failed to open wallet",) + log_has_string("joinmarket-yieldgenerator", "Failure to get blockheight",) ) # FIXME: use 'wait_for_unit' because 'create-web-index' always fails during startup due @@ -158,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",