"""
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"]))