bark/config.rs
1
2use std::fmt;
3use std::path::{Path, PathBuf};
4
5use anyhow::Context;
6use bitcoin::{FeeRate, Network};
7
8use bitcoin_ext::{BlockDelta, BlockHeight};
9
10
11/// Networks bark can be used on
12#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum BarkNetwork {
14 /// Bitcoin's mainnet
15 Mainnet,
16 /// The official Bitcoin Core signet
17 Signet,
18 /// Mutinynet
19 Mutinynet,
20 /// Any regtest network
21 Regtest,
22}
23
24impl BarkNetwork {
25 /// Map to the [Network] types
26 pub fn as_bitcoin(&self) -> Network {
27 match self {
28 Self::Mainnet => Network::Bitcoin,
29 Self::Signet => Network::Signet,
30 Self::Mutinynet => Network::Signet,
31 Self::Regtest => Network::Regtest,
32 }
33 }
34}
35
36impl fmt::Display for BarkNetwork {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 Self::Mainnet => f.write_str("mainnet"),
40 Self::Signet => f.write_str("signet"),
41 Self::Mutinynet => f.write_str("mutinynet"),
42 Self::Regtest => f.write_str("regtest"),
43 }
44 }
45}
46
47impl fmt::Debug for BarkNetwork {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 fmt::Display::fmt(self, f)
50 }
51}
52
53/// Configuration of the Bark wallet.
54///
55/// - [Config::esplora_address] or [Config::bitcoind_address] must be provided.
56/// - If you use [Config::bitcoind_address], you must also provide:
57/// - [Config::bitcoind_cookiefile] or
58/// - [Config::bitcoind_user] and [Config::bitcoind_pass]
59/// - Other optional fields can be omitted.
60///
61/// # Example
62/// Configure the wallet using defaults, then override endpoints for public signet:
63///
64/// ```rust
65/// use bark::Config;
66///
67/// let cfg = Config {
68/// server_address: "https://ark.signet.2nd.dev".into(),
69/// esplora_address: Some("https://esplora.signet.2nd.dev".into()),
70/// ..Config::network_default(bitcoin::Network::Bitcoin)
71/// };
72/// // cfg now has all other fields from the default configuration
73/// ```
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct Config {
76 /// The address of your ark server.
77 pub server_address: String,
78
79 /// The address of the Esplora HTTP REST server to use.
80 ///
81 /// Either this or the `bitcoind_address` field has to be provided.
82 pub esplora_address: Option<String>,
83
84 /// The address of the bitcoind RPC server to use.
85 ///
86 /// Either this or the `esplora_address` field has to be provided.
87 /// Either `bitcoind_cookiefile` or `bitcoind_user` and `bitcoind_pass` has to be provided.
88 pub bitcoind_address: Option<String>,
89
90 /// The path to the bitcoind rpc cookie file.
91 ///
92 /// Only used with `bitcoind_address`.
93 pub bitcoind_cookiefile: Option<PathBuf>,
94
95 /// The bitcoind RPC username.
96 ///
97 /// Only used with `bitcoind_address`.
98 pub bitcoind_user: Option<String>,
99
100 /// The bitcoind RPC password.
101 ///
102 /// Only used with `bitcoind_address`.
103 pub bitcoind_pass: Option<String>,
104
105 /// The number of blocks before expiration to refresh vtxos.
106 ///
107 /// Default value: 144 (24h) for mainnet, 12 for testnets
108 pub vtxo_refresh_expiry_threshold: BlockHeight,
109
110 /// An upper limit of the number of blocks we expect to need to
111 /// safely exit the vtxos.
112 ///
113 /// Default value: 12
114 pub vtxo_exit_margin: BlockDelta,
115
116 /// The number of blocks to claim a HTLC-recv VTXO.
117 ///
118 /// Default value: 18
119 pub htlc_recv_claim_delta: BlockDelta,
120
121 /// Optional SOCKS5 proxy URL for network traffic.
122 ///
123 /// The proxy is automatically bypassed for localhost addresses
124 /// (127.0.0.1, localhost, ::1), so a local bitcoind works without
125 /// extra configuration.
126 ///
127 /// Use `socks5h://` to resolve DNS through the proxy which is required for .onion addresses
128 /// and to prevent DNS leaks. We don't allow `socks5://` to be used to preserve privacy.
129 ///
130 /// Example: `socks5h://127.0.0.1:9050` for a local Tor daemon.
131 #[cfg(feature = "socks5-proxy")]
132 pub socks5_proxy: Option<String>,
133
134 /// A fallback fee rate to use in sat/kWu when we fail to retrieve a fee rate from the
135 /// configured bitcoind/esplora connection.
136 ///
137 /// Example for 1 sat/vB: --fallback-fee-rate 250
138 pub fallback_fee_rate: Option<FeeRate>,
139
140 /// The number of confirmations required before considering a round tx
141 /// fully confirmed
142 ///
143 /// Default value: 6 for mainnet, 2 for testnets
144 pub round_tx_required_confirmations: BlockHeight,
145
146 /// The number of confirmations required before considering an offboard tx
147 /// confirmed. If set to 0, offboard movements are marked as successful
148 /// immediately without waiting for confirmation.
149 ///
150 /// Default value: 2 for mainnet
151 pub offboard_required_confirmations: BlockHeight,
152
153 /// Daemon sync interval in seconds for fast tasks (lightning sync).
154 ///
155 /// This should be significantly smaller than the server's
156 /// `receive_htlc_forward_timeout` (default 30s). If the sync interval is
157 /// too close to the timeout, lightning receives are more likely to fail
158 /// because the client may not claim the HTLC in time.
159 ///
160 /// Default value: 1
161 pub daemon_fast_sync_interval_secs: u64,
162
163 /// Daemon sync interval in seconds for slow tasks (onchain, exits, boards, offboards, maintenance, rounds, mailbox).
164 ///
165 /// Default value: 60
166 pub daemon_slow_sync_interval_secs: u64,
167}
168
169impl Config {
170 /// A network-dependent default config that sets some useful defaults
171 ///
172 /// The [Default::default] provides a sane default for mainnet
173 pub fn network_default(network: Network) -> Self {
174 let mut ret = Self {
175 server_address: "http://127.0.0.1:3535".to_owned(),
176 esplora_address: None,
177 bitcoind_address: None,
178 bitcoind_cookiefile: None,
179 bitcoind_user: None,
180 bitcoind_pass: None,
181 #[cfg(feature = "socks5-proxy")]
182 socks5_proxy: None,
183 vtxo_refresh_expiry_threshold: 144,
184 vtxo_exit_margin: 12,
185 htlc_recv_claim_delta: 18,
186 fallback_fee_rate: None,
187 round_tx_required_confirmations: 2,
188 offboard_required_confirmations: 2,
189 daemon_fast_sync_interval_secs: 1,
190 daemon_slow_sync_interval_secs: 60,
191 };
192
193 if network != Network::Bitcoin {
194 ret.vtxo_refresh_expiry_threshold = 12;
195 ret.fallback_fee_rate = Some(FeeRate::from_sat_per_vb_unchecked(1));
196 ret.round_tx_required_confirmations = 1;
197 ret.offboard_required_confirmations = 0;
198 }
199
200 ret
201 }
202
203 /// Load config from the config file path, filling missing fields
204 /// from the network default.
205 ///
206 /// Config values are loaded in the following priority order (highest to lowest):
207 /// 1. Environment variables with `BARK_` prefix (e.g., `BARK_ESPLORA_ADDRESS`)
208 /// 2. Config file values
209 /// 3. Network defaults
210 pub fn load(network: Network, path: impl AsRef<Path>) -> anyhow::Result<Config> {
211 let default = config::Config::try_from(&Self::network_default(network))
212 .expect("default config failed to deconstruct");
213
214 Ok(config::Config::builder()
215 .add_source(default)
216 .add_source(config::File::from(path.as_ref()).required(false))
217 .add_source(config::Environment::with_prefix("BARK").separator("_"))
218 .build().context("error building config")?
219 .try_deserialize::<Config>().context("error parsing config")?)
220 }
221}
222