Source code for btc_networks

"""
Bitcoin Network Configuration Module

Provides configurations for Bitcoin mainnet and testnet,
including API endpoints for balance and transaction queries.
"""

from typing import Dict, Any, Optional
from dataclasses import dataclass


[docs] @dataclass class BTCNetworkConfig: """Immutable description of a single Bitcoin network and its block-explorer APIs. Bundles the human-readable name, the canonical ``network_name`` understood by ``bitcoinlib`` (e.g. ``bitcoin``, ``testnet``, ``signet``), a testnet flag, and the REST/explorer endpoints used to query balances, UTXOs, and transactions. Instances are declared once in the module-level ``BTC_NETWORKS`` registry and looked up by alias via ``get_btc_network``; the ``is_testnet`` flag and ``network_name`` then steer BIP84 coin-type selection (``0'`` vs ``1'``) in ``BTCWalletManager`` and the base URL drives HTTP calls in ``tools/btc_wallet_tools.py``. This is a frozen-by-convention config object with no behavior beyond ``to_dict``; it touches no Redis, network, or filesystem state itself. Args: name: Human-readable network label (e.g. ``Bitcoin Mainnet``). network_name: Canonical network identifier passed to ``bitcoinlib``. is_testnet: Whether this is a test/dev network (testnet/signet). api_base_url: Base URL for the primary REST API used for queries. api_backup_url: Optional secondary REST API URL. explorer_url: Optional human-facing block-explorer URL. """ name: str network_name: str is_testnet: bool api_base_url: str api_backup_url: Optional[str] = None explorer_url: Optional[str] = None
[docs] def to_dict(self) -> Dict[str, Any]: """Return a JSON-serializable view of this network config for API responses. Flattens the public fields (name, network_name, is_testnet, api_base_url, explorer_url) into a plain dict suitable for embedding in tool output or serializing to JSON; the ``api_backup_url`` is deliberately omitted as an internal failover detail. Performs no I/O and mutates nothing. No in-repo callers reference this method directly; it is a helper kept for serializing a config when surfacing network details. Returns: Dict[str, Any]: Mapping of the network's public fields. """ return { "name": self.name, "network_name": self.network_name, "is_testnet": self.is_testnet, "api_base_url": self.api_base_url, "explorer_url": self.explorer_url, }
BTC_NETWORKS: Dict[str, BTCNetworkConfig] = { "bitcoin": BTCNetworkConfig( name="Bitcoin Mainnet", network_name="bitcoin", is_testnet=False, api_base_url="https://blockstream.info/api", api_backup_url="https://blockchain.info", explorer_url="https://blockstream.info", ), "btc": BTCNetworkConfig( name="Bitcoin Mainnet", network_name="bitcoin", is_testnet=False, api_base_url="https://blockstream.info/api", api_backup_url="https://blockchain.info", explorer_url="https://blockstream.info", ), "mainnet": BTCNetworkConfig( name="Bitcoin Mainnet", network_name="bitcoin", is_testnet=False, api_base_url="https://blockstream.info/api", api_backup_url="https://blockchain.info", explorer_url="https://blockstream.info", ), "testnet": BTCNetworkConfig( name="Bitcoin Testnet", network_name="testnet", is_testnet=True, api_base_url="https://blockstream.info/testnet/api", explorer_url="https://blockstream.info/testnet", ), "btc_testnet": BTCNetworkConfig( name="Bitcoin Testnet", network_name="testnet", is_testnet=True, api_base_url="https://blockstream.info/testnet/api", explorer_url="https://blockstream.info/testnet", ), "signet": BTCNetworkConfig( name="Bitcoin Signet", network_name="signet", is_testnet=True, api_base_url="https://mempool.space/signet/api", explorer_url="https://mempool.space/signet", ), }
[docs] def get_btc_network(network_name: str = "bitcoin") -> Optional[BTCNetworkConfig]: """Look up a Bitcoin network config by name or alias from the static registry. Normalizes the supplied name (lowercased and stripped) and resolves it against the module-level ``BTC_NETWORKS`` mapping, which accepts several aliases per network (``bitcoin``, ``btc``, ``mainnet`` all map to mainnet; ``testnet``, ``btc_testnet``; ``signet``). This is the single entry point used to translate a user-facing network string into the explorer endpoints and BIP84 coin-type behavior the wallet code relies on. Pure in-memory lookup with no I/O. Called by ``BTCWalletManager`` methods (``create_wallet``, ``import_wif``, ``derive_address``, ``get_private_key`` in ``btc_wallet_manager.py``) and by the tool handlers in ``tools/btc_wallet_tools.py`` to validate and resolve the requested network before any wallet or HTTP operation. Args: network_name (str): Network name or alias; case-insensitive and whitespace is trimmed. Defaults to ``bitcoin``. Returns: Optional[BTCNetworkConfig]: The matching config, or ``None`` if the name is not a recognized alias. """ return BTC_NETWORKS.get(network_name.lower().strip())
[docs] def list_btc_networks(include_testnets: bool = True) -> list: """Enumerate the distinct supported Bitcoin networks, de-duplicating aliases. Walks the ``BTC_NETWORKS`` registry and collapses the multiple aliases that point at the same network down to one entry per ``network_name``, optionally filtering out test networks. Results are sorted so production (mainnet) appears before testnets and then alphabetically by display name, giving a stable list suitable for presenting available networks to a user. Pure in-memory operation with no I/O. No in-repo callers reference this helper directly; it is available for tools or surfaces that need to advertise the supported network set. Args: include_testnets (bool): When ``False``, omit testnet/signet entries and return only production networks. Defaults to ``True``. Returns: list: List of dicts, each with ``name``, ``network``, and ``is_testnet`` keys, one per distinct network, sorted mainnet-first then by name. """ seen = set() networks = [] for name, config in BTC_NETWORKS.items(): if config.network_name in seen: continue if not include_testnets and config.is_testnet: continue seen.add(config.network_name) networks.append( { "name": config.name, "network": config.network_name, "is_testnet": config.is_testnet, } ) return sorted(networks, key=lambda n: (n["is_testnet"], n["name"]))