github joinmarket-ng/joinmarket-ng 0.30.0

latest releases: 0.41.0, 0.40.0, 0.39.2...
5 months ago

Major improvements for wallet performance, specially for exiting old wallets with lots of transactions. Also security hardening, TUI improvements, and bug fixes.

Added

  • Add JSON-RPC batch helper to DescriptorWalletBackend to enable single-round-trip address lookups. (7dc89c31)
  • New wallet files are now encrypted with Argon2id (OWASP 2024 baseline parameters) instead of PBKDF2-HMAC-SHA256. Existing PBKDF2 wallet files continue to load unchanged. (5e6641de)
  • Cap per-tx fee rate at 1000 sat/vB by default (configurable via wallet.max_fee_rate_sat_vb) to prevent silent fee blowups from misconfigured estimators or oversized manual overrides. (041de9c0)
  • Auto-expand descriptor scan_range on wallet setup when used addresses sit near the lookahead boundary (76cbece3)
  • Replace the auto-expand descriptor scan_range probe and the --rescan-deep flag with a single 'jmwallet info --scan-depth N' one-shot recovery path that re-imports descriptors and rescans from genesis. The probe was unreliable for the migrated-wallet case it was meant to solve. (5984ef60)
  • Added Raspiblitz exit handler with B menu option and dynamic ESC behavior (8308f4f1)
  • TUI: Add configurable log level via [tui] log_level in config.toml (6780bc3f)
  • Add jm-wallet info --scan-status and jm-wallet rescan to (c340b993)
  • Orderbook watcher web UI now signals which makers are (461ef3a9)
  • Redesign the jm-wallet send confirmation summary for clearer field ordering and labels (21effc40)

Fixed

  • Speed up descriptor-wallet sync by replacing listaddressgroupings with listreceivedbyaddress, eliminating multi-minute hangs on wallets with many transactions or CoinJoin co-spends (11b118bd)
  • Validate destination address checksum and network in jmwallet direct_send (fixes regression of #196) (eb970809)
  • Require authentication on GET /api/v1/wallet/yieldgen/report so the maker earnings report is no longer readable without a bearer token. (e4a1a5e7)
  • Stop including fidelity bond proof in public reconnect announcements; bonds are now only sent via privmsg in response to !orderbook, matching the reference protocol. (519ffed2)
  • Fix maker incorrectly rejecting offers with 'Insufficient balance' when the dual-offer fee intersection falls in the dust band; the dominated offer is now suppressed cleanly with a clear log message (add613fd)
  • Fixed history header duplication and minor TUI issues (08230473)
  • Fixed maker menu TUI feedback and wallet info navigation (be50f914)
  • TUI: Capitalize "Fidelity Bonds" consistently across maker bond menus, prompts, and help text (c35df0db)
  • TUI: Show full testnet/signet/regtest fidelity bond addresses instead of dropping the network prefix (9ca785e7)
  • Restore bonds create improvements and add multi-network regex (94748205)
  • Fix fidelity-bond forgery where verify_bonds() did not bind the UTXO to the bond scriptPubKey (bdbac766)
  • Honor wallet.mnemonic_password from config when loading mnemonics via --mnemonic-file or MNEMONIC_FILE env (issue #498) (67043636)
  • Reject negative or out-of-range cjfee values in maker offers (009e0394)
  • Allow disabling broadcast on jm-wallet send via --no-broadcast (55a33edb)
  • Reject malformed amounts, mixdepths and counterparty counts on the wallet daemon HTTP API (9470db49)
  • Tighten JWT scope verification to reject cross-wallet tokens (f5371de6)
  • Fix tumbler runner crashing as failed instead of cancelled when stopped during a phase retry back-off (e6af7263)
  • Fix periodic summary notification reporting inflated earnings (42d2eeb9)
  • jm-wallet send now persists its change address in (b399a814)
  • TUI: Offer password storage for encrypted wallets in maker (36eb8df0)
  • Fix jam-ng and jmwalletd image builds broken by Debian trixie package version pins in the bookworm-based jam-builder stage (77c2ea5d)
  • Persist used addresses to prevent reissuing a previously funded deposit address after the UTXO is spent. (24e3e2f4)
  • Discover all wallet-owned addresses (including change) via paginated listtransactions to prevent missing them in deposit-address selection. (7d2cc00d)
  • Persist spent input addresses on transaction history rows to prevent deposit-address reuse across restarts (f98ffc5b)
  • TUI: Check for duplicate wallet name before import prompts (50d84ec1)

Configuration Changes

Existing config.toml files are not updated automatically. Review the bundled template changes below and apply the relevant options manually.

--- config.toml.template (0.29.0)
+++ config.toml.template (0.30.0)
@@ -151,8 +151,17 @@
 # Number of mixing depths (1-10)
 # mixdepth_count = 5

-# Address gap limit for wallet scanning (minimum: 6)
+# BIP44 gap limit: stop address scanning after this many consecutive empty
+# trailing addresses past the highest used one (minimum: 6, Electrum convention: 20).
+# Distinct from ``scan_range`` below.
 # gap_limit = 20
+
+# Initial descriptor scan range (max address index per branch) imported into
+# Bitcoin Core's descriptor wallet. Defaults to 1000, matching Bitcoin Core's
+# default keypool lookahead. Wallets migrated from legacy joinmarket-clientserver
+# may have used addresses beyond this; run ``jmwallet info --scan-depth N`` once
+# with a larger N to re-import descriptors and rescan from genesis (issue #475).
+# scan_range = 1000

 # Dust threshold in satoshis
 # dust_threshold = 27300
@@ -176,9 +185,21 @@
 # Default fee estimation settings
 # default_fee_block_target = 3  # Block target for wallet transactions

+# Safety cap on the fee rate (sat/vB) used for any wallet transaction.
+# Both manual fee rates and backend fee estimates above this cap are rejected
+# to protect against runaway-fee bugs and malicious/misconfigured fee oracles.
+# Increase only if you have a deliberate need to pay fees above 1000 sat/vB.
+# max_fee_rate_sat_vb = 1000
+
 # Mnemonic file settings (optional defaults)
 # mnemonic_file = ""            # Path to mnemonic file
-# mnemonic_password = ""        # Password for encrypted mnemonic
+#
+# SECURITY WARNING: mnemonic_password is stored IN PLAIN TEXT in this file.
+# Anyone who can read config.toml can decrypt the wallet, which defeats the
+# wallet's encryption. Only set it when you need unattended maker operation
+# and you trust the security of this machine. Prefer leaving it unset and
+# entering the password interactively when prompted by jm-wallet / jm-maker.
+# mnemonic_password = ""        # Password for encrypted mnemonic (plaintext!)

 # ============================================================================
 # Logging Settings
@@ -253,12 +274,12 @@
 [maker]
 # Minimum CoinJoin amount (in satoshis) that this maker will offer.
 #
-# There is no protocol-level minimum balance per mixdepth to run a maker;
-# 100000 ("100k sats") is a common orientation value for new users but is
-# not a requirement. Lower min_size lets you serve takers requesting
-# smaller mixes at the cost of producing smaller UTXOs.
-#
-# Default: DUST_THRESHOLD (the reference implementation's dust limit).
+# Lower min_size lets you serve takers requesting smaller mixes at the cost of
+# producing smaller UTXOs. Note: the *advertised* minsize is randomized on each
+# offer announcement (see size_factor below) and clamped to the dust threshold.
+#
+# Default: 100000 (matches the upstream JoinMarket reference; using a different
+# value may make jm-ng makers fingerprintable).
 # min_size = 100000

 # IMPORTANT: offer_type determines which fee setting is used.
@@ -273,17 +294,39 @@
 #   cj_fee_absolute = 500
 #
 # To run BOTH offer types simultaneously, use the CLI flag --dual-offers
+# In dual-offer mode the maker advertises one relative and one absolute
+# offer.  Their size ranges are split automatically at the fee
+# intersection x = cj_fee_absolute / cj_fee_relative:
+#   - absolute offer covers small CJs in [min_size, x]
+#     (guarantees a flat minimum profit per join)
+#   - relative offer covers large CJs in [x, max_balance]
+#     (scales fees with size, stays competitive on big mixes)
+# This produces a piecewise fee curve roughly equivalent to a linear
+# offer (min flat fee + proportional component) without breaking the
+# protocol.
 # offer_type = "sw0reloffer"

 # Fee settings (only one is used based on offer_type above)
-# cj_fee_relative = "0.001"  # 0.001 = 0.1% relative fee (for sw0reloffer)
-# cj_fee_absolute = 500      # Absolute fee in satoshis (for sw0absoffer)
-# tx_fee_contribution = 0    # Mining fee contribution in satoshis
+# Defaults match the upstream JoinMarket reference (yg-privacyenhanced) so that
+# jm-ng makers are not trivially distinguishable from reference makers.
+# cj_fee_relative = "0.00002"  # 0.00002 = 0.002% relative fee (for sw0reloffer)
+# cj_fee_absolute = 500        # Absolute fee in satoshis (for sw0absoffer)
+# tx_fee_contribution = 0      # Mining fee contribution in satoshis
+
+# Offer randomization factors. Each advertised offer is sampled uniformly from
+# [value*(1-factor), value*(1+factor)] (size is sampled downward only) on every
+# announcement so observers cannot correlate balance changes with exact values.
+# Set any factor to 0 to disable randomization for that field.
+# Defaults match the upstream JoinMarket yg-privacyenhanced reference.
+# cjfee_factor = 0.1
+# txfee_contribution_factor = 0.3
+# size_factor = 0.1

 # Minimum confirmations for UTXOs offered into coinjoins.
 # Default 0 lets the maker offer unconfirmed (mempool) UTXOs, which
-# improves liquidity. The PoDLE commitment lives on a separate UTXO
-# and is still gated by taker_utxo_age (taker side, default 5).
+# improves liquidity. The PoDLE commitment lives on a separate UTXO and
+# is still gated by taker_utxo_age (taker side, default 5). Raise this
+# to 1+ if you want to trade liquidity for RBF/eviction/reorg safety.
 # min_confirmations = 0

 # UTXO merge algorithm: "default", "gradual", "greedy", "random"
@@ -326,12 +369,22 @@
 # ============================================================================

 [taker]
-# Number of counterparty makers to use (1-20)
+# Number of counterparty makers to use (1-20).
+# When unset, a random value in [8, 10] is drawn for every CoinJoin (matches
+# the upstream JoinMarket sendpayment default and avoids fingerprinting via a
+# fixed counterparty count). Set explicitly to override.
 # counterparty_count = 10

 # Maximum acceptable coinjoin fees (paid to makers, not network/miner fees)
 # max_cj_fee_abs = 500        # Absolute fee in satoshis per maker
 # max_cj_fee_rel = "0.001"    # Relative fee (0.001 = 0.1%)
+
+# PoDLE commitment requirements (control which UTXOs can be used as PoDLE inputs).
+# These match the reference JoinMarket defaults; loosening them weakens the
+# anti-DoS commitment scheme.
+# taker_utxo_age = 5          # Minimum confirmations on the PoDLE input
+# taker_utxo_retries = 3      # Max PoDLE index retries per UTXO
+# taker_utxo_amtpercent = 20  # Min UTXO value as % of CJ amount

 # Network/miner transaction fee settings
 # fee_rate takes precedence over fee_block_target when set
@@ -357,8 +410,32 @@
 # tx_broadcast = "random-peer"
 # broadcast_peer_count = 3

-# Minimum number of makers required
-# minimum_makers = 1
+# Minimum number of makers required for a CoinJoin to proceed.
+# Default: 4 (matches the upstream JoinMarket reference POLICY default; using
+# minimum_makers=1 is fingerprintable and degrades the privacy of the join).
+# minimum_makers = 4
+
+# ============================================================================
+# Tumbler Settings
+# ============================================================================
+# These mirror ``tumbler.runner.RunnerContext`` fields that govern
+# inter-phase pacing. Production defaults are intentionally long so the
+# runner waits patiently for blockchain confirmations and UTXO age between
+# CoinJoin phases; tighten via env vars (TUMBLER__RETRY_DELAY_SECONDS, ...)
+# in test environments.
+
+[tumbler]
+# Minimum confirmations on a CoinJoin output before the next tumbler phase
+# may spend it. Set to 0 to disable the gate.
+# min_confirmations_between_phases = 6
+
+# Polling interval (seconds) for the confirmation gate.
+# confirmation_poll_interval = 30.0
+
+# Base delay (seconds) before retrying a failed taker phase. Applied with
+# linear backoff: ``retry_delay_seconds * attempt_count``. Set to 0 to retry
+# immediately.
+# retry_delay_seconds = 1800.0

 # ============================================================================
 # Directory Server Settings
@@ -418,3 +495,15 @@

 # Uptime tracking
 # uptime_grace_period = 60     # Seconds
+
+# ============================================================================
+# TUI Settings
+# ============================================================================
+
+[tui]
+# Log level for CLI output inside the TUI.
+# "WARNING" = only errors and warnings (default, clean output)
+# "INFO"    = show progress messages (e.g. "Connecting to directory servers...")
+# "DEBUG"   = verbose output for troubleshooting
+# Can be overridden per session: LOGGING__LEVEL=INFO jm-ng
+# log_level = "WARNING"

Don't miss a new joinmarket-ng release

NewReleases is sending notifications on new releases.