Skip to main content

stratum_apps/
tp_type.rs

1use crate::{config_helpers::opt_path_from_toml, key_utils::Secp256k1PublicKey};
2use std::path::PathBuf;
3
4/// Bitcoin network for determining node.sock location
5#[derive(Clone, Debug, serde::Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum BitcoinNetwork {
8    Mainnet,
9    Testnet4,
10    Signet,
11    Regtest,
12}
13
14impl BitcoinNetwork {
15    /// Returns the subdirectory name for this network.
16    /// Mainnet uses the root data directory.
17    fn subdir(&self) -> Option<&'static str> {
18        match self {
19            BitcoinNetwork::Mainnet => None,
20            BitcoinNetwork::Testnet4 => Some("testnet4"),
21            BitcoinNetwork::Signet => Some("signet"),
22            BitcoinNetwork::Regtest => Some("regtest"),
23        }
24    }
25}
26
27/// Returns the default Bitcoin Core data directory for the current OS.
28fn default_bitcoin_data_dir() -> Option<PathBuf> {
29    #[cfg(target_os = "linux")]
30    {
31        dirs::home_dir().map(|h| h.join(".bitcoin"))
32    }
33    #[cfg(target_os = "macos")]
34    {
35        dirs::home_dir().map(|h| h.join("Library/Application Support/Bitcoin"))
36    }
37    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
38    {
39        None
40    }
41}
42
43/// Resolves the IPC socket path from network and optional data_dir.
44/// Constructs path from network + optional data_dir (or OS default).
45///
46/// Returns `None` if data_dir cannot be determined (neither provided nor OS default available).
47pub fn resolve_ipc_socket_path(
48    network: &BitcoinNetwork,
49    data_dir: Option<PathBuf>,
50) -> Option<PathBuf> {
51    let base_dir = data_dir.or_else(default_bitcoin_data_dir)?;
52
53    Some(match network.subdir() {
54        Some(subdir) => base_dir.join(subdir).join("node.sock"),
55        None => base_dir.join("node.sock"),
56    })
57}
58
59/// Which type of Template Provider will be used,
60/// along with the relevant config parameters for each.
61#[derive(Clone, Debug, serde::Deserialize)]
62pub enum TemplateProviderType {
63    Sv2Tp {
64        address: String,
65        public_key: Option<Secp256k1PublicKey>,
66    },
67    BitcoinCoreIpc {
68        /// Network for determining socket path subdirectory.
69        network: BitcoinNetwork,
70        /// Custom Bitcoin data directory. Uses OS default if not set.
71        #[serde(default, deserialize_with = "opt_path_from_toml")]
72        data_dir: Option<PathBuf>,
73        fee_threshold: u64,
74        min_interval: u8,
75    },
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn network_with_data_dir_mainnet() {
84        let result =
85            resolve_ipc_socket_path(&BitcoinNetwork::Mainnet, Some(PathBuf::from("/data")));
86        assert_eq!(result, Some(PathBuf::from("/data/node.sock")));
87    }
88
89    #[test]
90    fn network_with_data_dir_regtest() {
91        let result =
92            resolve_ipc_socket_path(&BitcoinNetwork::Regtest, Some(PathBuf::from("/data")));
93        assert_eq!(result, Some(PathBuf::from("/data/regtest/node.sock")));
94    }
95
96    #[test]
97    fn network_with_data_dir_signet() {
98        let result = resolve_ipc_socket_path(&BitcoinNetwork::Signet, Some(PathBuf::from("/data")));
99        assert_eq!(result, Some(PathBuf::from("/data/signet/node.sock")));
100    }
101
102    #[test]
103    fn network_with_data_dir_testnet4() {
104        let result =
105            resolve_ipc_socket_path(&BitcoinNetwork::Testnet4, Some(PathBuf::from("/data")));
106        assert_eq!(result, Some(PathBuf::from("/data/testnet4/node.sock")));
107    }
108
109    #[test]
110    fn missing_data_dir_uses_os_default() {
111        // This test verifies behavior when data_dir is None
112        // Result depends on OS - will be Some on Linux/macOS, None on unsupported OS
113        let result = resolve_ipc_socket_path(&BitcoinNetwork::Regtest, None);
114        #[cfg(any(target_os = "linux", target_os = "macos"))]
115        assert!(result.is_some());
116        #[cfg(not(any(target_os = "linux", target_os = "macos")))]
117        assert!(result.is_none());
118    }
119}