-
Notifications
You must be signed in to change notification settings - Fork 94
Expand file tree
/
Copy pathconfig.py
More file actions
117 lines (99 loc) · 3.38 KB
/
config.py
File metadata and controls
117 lines (99 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# -*- coding: utf-8 -*-
"""
Python Bitcoin Utils CLI — configuration management.
Handles network selection, fee estimation defaults, and tool preferences.
"""
import json
from pathlib import Path
from dataclasses import dataclass, asdict
from typing import Optional
_BASE_DIR = Path(__file__).resolve().parent
_CONFIG_FILE = _BASE_DIR / "btc_config.json"
NETWORKS = {
"mainnet": {
"name": "Bitcoin Mainnet",
"prefix_p2pkh": 0x00,
"prefix_p2sh": 0x05,
"bech32_hrp": "bc",
"wif_prefix": 0x80,
"explorer": "https://blockstream.info",
},
"testnet": {
"name": "Bitcoin Testnet3",
"prefix_p2pkh": 0x6F,
"prefix_p2sh": 0xC4,
"bech32_hrp": "tb",
"wif_prefix": 0xEF,
"explorer": "https://blockstream.info/testnet",
},
"signet": {
"name": "Bitcoin Signet",
"prefix_p2pkh": 0x6F,
"prefix_p2sh": 0xC4,
"bech32_hrp": "tb",
"wif_prefix": 0xEF,
"explorer": "https://mutinynet.com",
},
}
DEFAULT_NETWORK = "testnet"
DEFAULT_FEE_RATE = 10
DEFAULT_DUST_LIMIT = 546
DEFAULT_RBF = True
@dataclass
class BtcConfig:
network: str = DEFAULT_NETWORK
fee_rate_sat_vb: int = DEFAULT_FEE_RATE
dust_limit_sat: int = DEFAULT_DUST_LIMIT
enable_rbf: bool = DEFAULT_RBF
address_type: str = "bech32"
explorer_url: str = NETWORKS[DEFAULT_NETWORK]["explorer"]
log_level: str = "INFO"
auto_broadcast: bool = False
rpc_host: Optional[str] = None
rpc_port: Optional[int] = None
rpc_user: Optional[str] = None
@property
def network_params(self) -> dict:
return NETWORKS.get(self.network, NETWORKS["testnet"])
def validate(self) -> list[str]:
errors = []
if self.network not in NETWORKS:
errors.append(f"Unknown network: {self.network}")
if self.fee_rate_sat_vb < 1:
errors.append("Fee rate must be >= 1 sat/vB")
if self.dust_limit_sat < 0:
errors.append("Dust limit cannot be negative")
if self.address_type not in ("legacy", "p2sh-segwit", "bech32", "bech32m"):
errors.append(f"Unknown address type: {self.address_type}")
return errors
def load_config() -> BtcConfig:
"""Load configuration from disk or return defaults."""
if not _CONFIG_FILE.exists():
cfg = BtcConfig()
save_config(cfg)
return cfg
with open(_CONFIG_FILE, "r", encoding="utf-8") as fp:
raw = json.load(fp)
cfg = BtcConfig()
for k, v in raw.items():
if hasattr(cfg, k):
setattr(cfg, k, v)
return cfg
def save_config(cfg: BtcConfig) -> None:
"""Persist configuration to disk."""
with open(_CONFIG_FILE, "w", encoding="utf-8") as fp:
json.dump(asdict(cfg), fp, indent=2, ensure_ascii=False)
def reset_config() -> BtcConfig:
"""Reset to defaults."""
cfg = BtcConfig()
save_config(cfg)
return cfg
def switch_network(network: str) -> Optional[str]:
"""Switch active network. Returns error string or None."""
if network not in NETWORKS:
return f"Unknown network '{network}'. Available: {', '.join(NETWORKS.keys())}"
cfg = load_config()
cfg.network = network
cfg.explorer_url = NETWORKS[network]["explorer"]
save_config(cfg)
return None