spark_rust/lib.rs
1//! # Spark Wallet SDK
2//!
3//! A Rust implementation of the Spark protocol wallet, providing secure Bitcoin and Lightning
4//! Network operations through the Spark Service Provider (SSP).
5//!
6//! This crate forms a complete wallet with all necessary Spark utilities, built on secure
7//! cryptographic primitives from the [spark-cryptography](https://github.com/polarityorg/spark-rs/tree/main/crates/spark-cryptography) crate.
8//!
9//! ## Overview
10//!
11//! Spark is a protocol that allows secure, non-custodial Bitcoin and Lightning Network operations,
12//! with all critical cryptographic operations performed client-side while leveraging Spark Service Providers
13//! for efficient network operations. This implementation provides:
14//!
15//! - Complete Bitcoin wallet functionality
16//! - Lightning Network payments (send and receive)
17//! - Funds transfers between Spark users
18//! - Deposit and withdrawal capabilities
19//! - Comprehensive key management and signing utilities
20//!
21//! ## Architecture
22//!
23//! The Spark Wallet SDK comprises five main components:
24//!
25//! - **config**: Configuration settings for the Spark wallet
26//! - **handlers**: User-facing APIs for wallet operations
27//! - **internal_handlers**: Service handlers for signing processes and RPC communications
28//! - **rpc**: Client for establishing secure connections to Spark nodes
29//! - **signer**: Key management, storage, and signing capabilities
30//!
31//! ## Quick Start
32//!
33//! ```rust
34//! use spark_rust::SparkSdk;
35//! use spark_rust::SparkNetwork;
36//! use spark_rust::signer::default_signer::DefaultSigner;
37//! use std::time::Duration;
38//!
39//! #[tokio::main]
40//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
41//! // Initialize the default signer with a mnemonic phrase
42//! let mnemonic = "abandon ability able about above absent absorb abstract absurd abuse access accident";
43//! let network = SparkNetwork::Regtest;
44//! let default_signer = DefaultSigner::from_mnemonic(mnemonic, network).await?;
45//!
46//! // Create a new SDK instance
47//! let sdk = SparkSdk::new(network, default_signer).await?;
48//!
49//! // Generate a deposit address
50//! let deposit_address = sdk.generate_deposit_address().await?;
51//! println!("Deposit address: {}", deposit_address.deposit_address);
52//!
53//! // Query pending transfers
54//! let pending = sdk.query_pending_transfers().await?;
55//! println!("You have {} pending transfers", pending.len());
56//!
57//! // Claim all pending transfers
58//! sdk.claim_transfers().await?;
59//!
60//! // Get your wallet balance
61//! let balance = sdk.get_bitcoin_balance();
62//! println!("Your balance is {} satoshis", balance);
63//!
64//! Ok(())
65//! }
66//! ```
67//!
68//! ## Security Model
69//!
70//! Spark operates with a security model that ensures users always maintain control of their funds:
71//!
72//! - All critical keys are managed client-side
73//! - Threshold signing with FROST ensures security with multiple participants
74//! - Transactions require both user and Spark operator signatures
75//! - The user always initiates the signing process
76//!
77//! ## Fee Structure
78//!
79//! Unlike traditional Bitcoin wallets, Spark uses a service fee model where:
80//!
81//! - Fees are charged by the Spark Service Provider (SSP) for operations they perform
82//! - You don't directly pay Bitcoin mining fees (these are handled by the SSP)
83//! - Fee estimation methods help you determine costs before performing operations
84//!
85//! Common fee operations include:
86//! - Lightning payments (sending and receiving)
87//! - Leaves swaps (optimizing wallet structure)
88//! - Cooperative exits (withdrawing to on-chain Bitcoin)
89//!
90//! ## Custom Signers
91//!
92//! While the SDK provides a default in-memory signer implementation, advanced users can
93//! implement their own signers for specialized use cases by implementing the `SparkSigner`
94//! trait and its associated sub-traits.
95//!
96//! ```rust
97//! use spark_rust::signer::traits::SparkSigner;
98//! use spark_rust::SparkNetwork;
99//!
100//! // Example of using a custom signer (implementation details omitted)
101//! #[tokio::main]
102//! async fn main() {
103//! // my_custom_signer implements the SparkSigner trait
104//! let my_custom_signer = create_custom_signer().await.unwrap();
105//! let sdk = SparkSdk::new(SparkNetwork::Regtest, my_custom_signer).await.unwrap();
106//! // ...
107//! }
108//! # async fn create_custom_signer() -> Result<impl SparkSigner, Box<dyn std::error::Error>> { todo!() }
109//! ```
110
111use core::fmt;
112use std::str::FromStr;
113pub(crate) mod constants;
114pub mod error;
115pub(crate) mod wallet;
116
117pub mod signer;
118
119// pub use wallet::utils;
120pub use wallet::handlers::cooperative_exit::{CompleteCoopExitInput, RequestCoopExitInput};
121pub use wallet::SparkSdk;
122
123pub(crate) mod common_types;
124
125#[cfg(any(test, feature = "integration-tests"))]
126pub mod spark_test_utils;
127
128pub mod rpc;
129
130/// Spark Network. This is the network of the Spark Operators that the user chooses to connect to.
131///
132/// - `Mainnet` is the Bitcoin network, and all operations involve real money.
133/// - `Regtest` is Lightspark's Regtest network for testing purposes.
134#[derive(Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
135#[non_exhaustive]
136pub enum SparkNetwork {
137 /// Mainnet Bitcoin network. Use this for real transactions with actual value.
138 Mainnet,
139
140 /// Lightspark's Regtest network for testing and development.
141 /// No real value is transferred when using this network.
142 Regtest,
143}
144
145impl SparkNetwork {
146 /// Converts a Spark network to its corresponding Bitcoin network.
147 ///
148 /// # Returns
149 /// The equivalent Bitcoin network type from the `bitcoin` crate:
150 /// - `SparkNetwork::Mainnet` returns `bitcoin::Network::Bitcoin`
151 /// - `SparkNetwork::Regtest` returns `bitcoin::Network::Regtest`
152 pub fn to_bitcoin_network(&self) -> ::bitcoin::Network {
153 match self {
154 SparkNetwork::Mainnet => ::bitcoin::Network::Bitcoin,
155 SparkNetwork::Regtest => ::bitcoin::Network::Regtest,
156 }
157 }
158
159 /// Marshals the network type to its protocol buffer representation. For most use cases, you don't need to use this method.
160 ///
161 /// # Returns
162 /// An integer representing the network in the protocol buffer format.
163 pub fn marshal_proto(&self) -> i32 {
164 match self {
165 SparkNetwork::Mainnet => spark_protos::spark::Network::Mainnet as i32,
166 SparkNetwork::Regtest => spark_protos::spark::Network::Regtest as i32,
167 }
168 }
169}
170
171impl FromStr for SparkNetwork {
172 type Err = String;
173
174 /// Creates a `SparkNetwork` from a string representation.
175 ///
176 /// The string comparison is case-insensitive, so both "mainnet" and "Mainnet"
177 /// will return `SparkNetwork::Mainnet`.
178 ///
179 /// # Arguments
180 /// * `s` - A string slice representing the network ("mainnet" or "regtest")
181 ///
182 /// # Returns
183 /// * `Ok(SparkNetwork)` if the string matches a known network
184 /// * `Err(String)` with an error message if the string does not match
185 ///
186 /// # Examples
187 /// ```
188 /// use std::str::FromStr;
189 /// use spark_rust::SparkNetwork;
190 ///
191 /// let mainnet = SparkNetwork::from_str("mainnet").unwrap();
192 /// assert_eq!(mainnet, SparkNetwork::Mainnet);
193 ///
194 /// let regtest = SparkNetwork::from_str("Regtest").unwrap();
195 /// assert_eq!(regtest, SparkNetwork::Regtest);
196 ///
197 /// let err = SparkNetwork::from_str("testnet");
198 /// assert!(err.is_err());
199 /// ```
200 fn from_str(s: &str) -> Result<Self, String> {
201 match s.to_lowercase().as_str() {
202 "mainnet" => Ok(SparkNetwork::Mainnet),
203 "regtest" => Ok(SparkNetwork::Regtest),
204 _ => Err(format!("Invalid network: {}", s)),
205 }
206 }
207}
208
209impl fmt::Display for SparkNetwork {
210 /// Formats the network as a lowercase string.
211 ///
212 /// # Returns
213 /// - `"mainnet"` for `SparkNetwork::Mainnet`
214 /// - `"regtest"` for `SparkNetwork::Regtest`
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 match self {
217 SparkNetwork::Mainnet => write!(f, "mainnet"),
218 SparkNetwork::Regtest => write!(f, "regtest"),
219 }
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use std::str::FromStr as _;
226
227 use crate::SparkNetwork;
228
229 #[test]
230 fn test_spark_network_display() {
231 let network = SparkNetwork::Mainnet;
232 assert_eq!(network.to_string(), "mainnet");
233
234 let network = SparkNetwork::Regtest;
235 assert_eq!(network.to_string(), "regtest");
236 }
237
238 #[test]
239 fn test_spark_network_from_str() {
240 let network = SparkNetwork::from_str("mainnet").unwrap();
241 assert_eq!(network, SparkNetwork::Mainnet);
242 let network = SparkNetwork::from_str("Mainnet").unwrap();
243 assert_eq!(network, SparkNetwork::Mainnet);
244
245 let network = SparkNetwork::from_str("regtest").unwrap();
246 assert_eq!(network, SparkNetwork::Regtest);
247 let network = SparkNetwork::from_str("Regtest").unwrap();
248 assert_eq!(network, SparkNetwork::Regtest);
249
250 let network = SparkNetwork::from_str("testnet");
251 assert!(network.is_err());
252 }
253}