Module jmcore.paths

Shared path utilities for JoinMarket data directories.

This module provides consistent path handling across all JoinMarket components (maker, taker, wallet) for data directories, commitment blacklists, and history.

Functions

def get_all_nick_states(data_dir: Path | str | None = None) ‑> dict[str, str]
Expand source code
def get_all_nick_states(data_dir: Path | str | None = None) -> dict[str, str]:
    """
    Read all component nick state files from the data directory.

    Useful for discovering all running components and their nicks.

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())

    Returns:
        Dict mapping component names to their nicks (e.g., {'maker': 'J5XXX', 'taker': 'J5YYY'})
    """
    if data_dir is None:
        data_dir = get_default_data_dir()
    elif isinstance(data_dir, str):
        data_dir = Path(data_dir)

    state_dir = data_dir / "state"
    if not state_dir.exists():
        return {}

    result: dict[str, str] = {}
    for path in state_dir.glob("*.nick"):
        component = path.stem  # e.g., 'maker' from 'maker.nick'
        try:
            nick = path.read_text().strip()
            if nick:
                result[component] = nick
        except OSError:
            continue

    return result

Read all component nick state files from the data directory.

Useful for discovering all running components and their nicks.

Args

data_dir
Optional data directory (defaults to get_default_data_dir())

Returns

Dict mapping component names to their nicks (e.g., {'maker': 'J5XXX', 'taker': 'J5YYY'})

def get_commitment_blacklist_path(data_dir: Path | None = None) ‑> pathlib.Path
Expand source code
def get_commitment_blacklist_path(data_dir: Path | None = None) -> Path:
    """
    Get the path to the commitment blacklist file.

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())

    Returns:
        Path to cmtdata/commitmentlist (compatible with reference JoinMarket)
    """
    if data_dir is None:
        data_dir = get_default_data_dir()

    # Use cmtdata/ subdirectory for commitment data (matches reference implementation)
    cmtdata_dir = data_dir / "cmtdata"
    cmtdata_dir.mkdir(parents=True, exist_ok=True)

    return cmtdata_dir / "commitmentlist"

Get the path to the commitment blacklist file.

Args

data_dir
Optional data directory (defaults to get_default_data_dir())

Returns

Path to cmtdata/commitmentlist (compatible with reference JoinMarket)

def get_default_data_dir() ‑> pathlib.Path
Expand source code
def get_default_data_dir() -> Path:
    """
    Get the default JoinMarket data directory.

    Returns ~/.joinmarket-ng or $JOINMARKET_DATA_DIR if set.
    Creates the directory if it doesn't exist.

    For compatibility with reference JoinMarket in Docker, users can
    set JOINMARKET_DATA_DIR=/home/jm/.joinmarket-ng to share the same volume.
    """
    env_path = os.getenv("JOINMARKET_DATA_DIR")
    data_dir = Path(env_path) if env_path else Path.home() / ".joinmarket-ng"

    data_dir.mkdir(parents=True, exist_ok=True)
    return data_dir

Get the default JoinMarket data directory.

Returns ~/.joinmarket-ng or $JOINMARKET_DATA_DIR if set. Creates the directory if it doesn't exist.

For compatibility with reference JoinMarket in Docker, users can set JOINMARKET_DATA_DIR=/home/jm/.joinmarket-ng to share the same volume.

def get_ignored_makers_path(data_dir: Path | None = None) ‑> pathlib.Path
Expand source code
def get_ignored_makers_path(data_dir: Path | None = None) -> Path:
    """
    Get the path to the ignored makers file (for takers).

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())

    Returns:
        Path to ignored_makers.txt
    """
    if data_dir is None:
        data_dir = get_default_data_dir()

    return data_dir / "ignored_makers.txt"

Get the path to the ignored makers file (for takers).

Args

data_dir
Optional data directory (defaults to get_default_data_dir())

Returns

Path to ignored_makers.txt

def get_nick_state_path(data_dir: Path | str | None = None, component: str = '') ‑> pathlib.Path
Expand source code
def get_nick_state_path(data_dir: Path | str | None = None, component: str = "") -> Path:
    """
    Get the path to a component's nick state file.

    The nick state file stores the current nick of a running component,
    allowing operators to easily identify the nick and enabling cross-component
    protection (e.g., taker excluding own maker nick from peer selection).

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())
        component: Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')

    Returns:
        Path to state/<component>.nick (e.g., ~/.joinmarket-ng/state/maker.nick)
    """
    if data_dir is None:
        data_dir = get_default_data_dir()
    elif isinstance(data_dir, str):
        data_dir = Path(data_dir)

    # Use state/ subdirectory to keep state files organized
    state_dir = data_dir / "state"
    state_dir.mkdir(parents=True, exist_ok=True)

    return state_dir / f"{component}.nick"

Get the path to a component's nick state file.

The nick state file stores the current nick of a running component, allowing operators to easily identify the nick and enabling cross-component protection (e.g., taker excluding own maker nick from peer selection).

Args

data_dir
Optional data directory (defaults to get_default_data_dir())
component
Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')

Returns

Path to state/.nick (e.g., ~/.joinmarket-ng/state/maker.nick)

def get_used_commitments_path(data_dir: Path | None = None) ‑> pathlib.Path
Expand source code
def get_used_commitments_path(data_dir: Path | None = None) -> Path:
    """
    Get the path to the used commitments file (for takers).

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())

    Returns:
        Path to cmtdata/commitments.json (compatible with reference JoinMarket)
    """
    if data_dir is None:
        data_dir = get_default_data_dir()

    # Use cmtdata/ subdirectory
    cmtdata_dir = data_dir / "cmtdata"
    cmtdata_dir.mkdir(parents=True, exist_ok=True)

    return cmtdata_dir / "commitments.json"

Get the path to the used commitments file (for takers).

Args

data_dir
Optional data directory (defaults to get_default_data_dir())

Returns

Path to cmtdata/commitments.json (compatible with reference JoinMarket)

def read_nick_state(data_dir: Path | str | None, component: str) ‑> str | None
Expand source code
def read_nick_state(data_dir: Path | str | None, component: str) -> str | None:
    """
    Read a component's nick from its state file.

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())
        component: Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')

    Returns:
        The nick string if file exists and is readable, None otherwise
    """
    if data_dir is None:
        data_dir = get_default_data_dir()
    elif isinstance(data_dir, str):
        data_dir = Path(data_dir)

    path = get_nick_state_path(data_dir, component)
    if path.exists():
        try:
            return path.read_text().strip()
        except OSError:
            return None
    return None

Read a component's nick from its state file.

Args

data_dir
Optional data directory (defaults to get_default_data_dir())
component
Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')

Returns

The nick string if file exists and is readable, None otherwise

def remove_nick_state(data_dir: Path | str | None, component: str) ‑> bool
Expand source code
def remove_nick_state(data_dir: Path | str | None, component: str) -> bool:
    """
    Remove a component's nick state file (e.g., on shutdown).

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())
        component: Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')

    Returns:
        True if file was removed, False if it didn't exist or removal failed
    """
    if data_dir is None:
        data_dir = get_default_data_dir()
    elif isinstance(data_dir, str):
        data_dir = Path(data_dir)

    path = get_nick_state_path(data_dir, component)
    if path.exists():
        try:
            path.unlink()
            return True
        except OSError:
            return False
    return False

Remove a component's nick state file (e.g., on shutdown).

Args

data_dir
Optional data directory (defaults to get_default_data_dir())
component
Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')

Returns

True if file was removed, False if it didn't exist or removal failed

def write_nick_state(data_dir: Path | str | None, component: str, nick: str) ‑> pathlib.Path
Expand source code
def write_nick_state(data_dir: Path | str | None, component: str, nick: str) -> Path:
    """
    Write a component's nick to its state file.

    Creates the state directory if it doesn't exist.

    Args:
        data_dir: Optional data directory (defaults to get_default_data_dir())
        component: Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')
        nick: The nick to write (e.g., 'J5XXXXXXXXX')

    Returns:
        Path to the written state file
    """
    path = get_nick_state_path(data_dir, component)
    path.write_text(nick + "\n")
    return path

Write a component's nick to its state file.

Creates the state directory if it doesn't exist.

Args

data_dir
Optional data directory (defaults to get_default_data_dir())
component
Component name (e.g., 'maker', 'taker', 'directory', 'orderbook')
nick
The nick to write (e.g., 'J5XXXXXXXXX')

Returns

Path to the written state file