spark_rust/
lib.rs

1//! # Spark Wallet SDK
2//!
3//! This crate forms a complete wallet with all necessary Spark utilities.
4
5use core::fmt;
6use std::str::FromStr;
7pub(crate) mod constants;
8pub mod error;
9pub(crate) mod wallet;
10
11pub mod signer;
12
13use error::{SparkSdkError, ValidationError};
14// pub use wallet::utils;
15pub use wallet::handlers::cooperative_exit::{CompleteCoopExitInput, RequestCoopExitInput};
16pub use wallet::handlers::transfer::Transfer;
17pub use wallet::SparkSdk;
18
19pub(crate) mod common_types;
20
21#[cfg(any(test, feature = "integration-tests"))]
22pub mod spark_test_utils;
23
24pub mod rpc;
25
26/// Spark Network. This is the network of the Spark Operators that the user chooses to connect to.
27///
28/// - `Mainnet` is the Bitcoin network, and all operations involve real money.
29/// - `Regtest` is Lightspark's Regtest network for testing purposes.
30#[derive(Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
31#[non_exhaustive]
32pub enum SparkNetwork {
33    /// Mainnet Bitcoin network. Use this for real transactions with actual value.
34    Mainnet,
35
36    /// Lightspark's Regtest network for testing and development.
37    /// No real value is transferred when using this network.
38    Regtest,
39}
40
41impl SparkNetwork {
42    /// Converts a Spark network to its corresponding Bitcoin network.
43    ///
44    /// # Returns
45    /// The equivalent Bitcoin network type from the `bitcoin` crate:
46    /// - `SparkNetwork::Mainnet` returns `bitcoin::Network::Bitcoin`
47    /// - `SparkNetwork::Regtest` returns `bitcoin::Network::Regtest`
48    pub fn to_bitcoin_network(&self) -> ::bitcoin::Network {
49        match self {
50            SparkNetwork::Mainnet => ::bitcoin::Network::Bitcoin,
51            SparkNetwork::Regtest => ::bitcoin::Network::Regtest,
52        }
53    }
54
55    /// Marshals the network type to its protocol buffer representation. For most use cases, you don't need to use this method.
56    ///
57    /// # Returns
58    /// An integer representing the network in the protocol buffer format.
59    pub fn marshal_proto(&self) -> i32 {
60        match self {
61            SparkNetwork::Mainnet => spark_protos::spark::Network::Mainnet as i32,
62            SparkNetwork::Regtest => spark_protos::spark::Network::Regtest as i32,
63        }
64    }
65}
66
67impl TryFrom<i32> for SparkNetwork {
68    type Error = SparkSdkError;
69
70    fn try_from(value: i32) -> Result<Self, SparkSdkError> {
71        spark_protos::spark::Network::try_from(value)
72            .map_err(|_| {
73                SparkSdkError::from(ValidationError::InvalidBitcoinNetwork(value.to_string()))
74            })
75            .and_then(|network| network.try_into())
76    }
77}
78
79impl TryFrom<spark_protos::spark::Network> for SparkNetwork {
80    type Error = SparkSdkError;
81
82    fn try_from(value: spark_protos::spark::Network) -> Result<Self, SparkSdkError> {
83        match value {
84            spark_protos::spark::Network::Mainnet => Ok(SparkNetwork::Mainnet),
85            spark_protos::spark::Network::Regtest => Ok(SparkNetwork::Regtest),
86            _ => Err(SparkSdkError::from(ValidationError::InvalidBitcoinNetwork(
87                value.as_str_name().to_string(),
88            ))),
89        }
90    }
91}
92
93impl FromStr for SparkNetwork {
94    type Err = String;
95
96    /// Creates a `SparkNetwork` from a string representation.
97    ///
98    /// The string comparison is case-insensitive, so both "mainnet" and "Mainnet"
99    /// will return `SparkNetwork::Mainnet`.
100    ///
101    /// # Arguments
102    /// * `s` - A string slice representing the network ("mainnet" or "regtest")
103    ///
104    /// # Returns
105    /// * `Ok(SparkNetwork)` if the string matches a known network
106    /// * `Err(String)` with an error message if the string does not match
107    ///
108    /// # Examples
109    /// ```
110    /// use std::str::FromStr;
111    /// use spark_rust::SparkNetwork;
112    ///
113    /// let mainnet = SparkNetwork::from_str("mainnet").unwrap();
114    /// assert_eq!(mainnet, SparkNetwork::Mainnet);
115    ///
116    /// let regtest = SparkNetwork::from_str("Regtest").unwrap();
117    /// assert_eq!(regtest, SparkNetwork::Regtest);
118    ///
119    /// let err = SparkNetwork::from_str("testnet");
120    /// assert!(err.is_err());
121    /// ```
122    fn from_str(s: &str) -> Result<Self, String> {
123        match s.to_lowercase().as_str() {
124            "mainnet" => Ok(SparkNetwork::Mainnet),
125            "regtest" => Ok(SparkNetwork::Regtest),
126            _ => Err(format!("Invalid network: {}", s)),
127        }
128    }
129}
130
131impl fmt::Display for SparkNetwork {
132    /// Formats the network as a lowercase string.
133    ///
134    /// # Returns
135    /// - `"mainnet"` for `SparkNetwork::Mainnet`
136    /// - `"regtest"` for `SparkNetwork::Regtest`
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        match self {
139            SparkNetwork::Mainnet => write!(f, "mainnet"),
140            SparkNetwork::Regtest => write!(f, "regtest"),
141        }
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use std::str::FromStr as _;
148
149    use crate::SparkNetwork;
150
151    #[test]
152    fn test_spark_network_display() {
153        let network = SparkNetwork::Mainnet;
154        assert_eq!(network.to_string(), "mainnet");
155
156        let network = SparkNetwork::Regtest;
157        assert_eq!(network.to_string(), "regtest");
158    }
159
160    #[test]
161    fn test_spark_network_from_str() {
162        let network = SparkNetwork::from_str("mainnet").unwrap();
163        assert_eq!(network, SparkNetwork::Mainnet);
164        let network = SparkNetwork::from_str("Mainnet").unwrap();
165        assert_eq!(network, SparkNetwork::Mainnet);
166
167        let network = SparkNetwork::from_str("regtest").unwrap();
168        assert_eq!(network, SparkNetwork::Regtest);
169        let network = SparkNetwork::from_str("Regtest").unwrap();
170        assert_eq!(network, SparkNetwork::Regtest);
171
172        let network = SparkNetwork::from_str("testnet");
173        assert!(network.is_err());
174    }
175}