spark_sdk/wallet/handlers/
lightning.rs1use std::collections::HashMap;
2
3use lightning_invoice::Bolt11Invoice;
4use rand::rngs::OsRng;
5use spark_cryptography::secret_sharing::shamir_new::VSS;
6use spark_protos::spark::StorePreimageShareRequest;
7
8use crate::{
9 error::SparkSdkError,
10 signer::traits::{secp256k1::KeygenMethod, SparkSigner},
11 wallet::{
12 internal_handlers::traits::{
13 leaves::LeavesInternalHandlers,
14 lightning::LightningInternalHandlers,
15 ssp::SspInternalHandlers,
16 transfer::{LeafKeyTweak, TransferInternalHandlers},
17 },
18 leaf_manager::LeafNodeStatus,
19 },
20 SparkSdk,
21};
22
23impl<S: SparkSigner + Send + Sync + Clone + 'static> SparkSdk<S> {
24 pub async fn pay_lightning_invoice(
25 &self,
26 invoice: String,
27 amount_sats: u64,
28 ) -> Result<String, SparkSdkError> {
29 let bolt11_invoice: Bolt11Invoice = invoice.parse().unwrap();
31
32 let invoice_amount_msats =
34 bolt11_invoice
35 .amount_milli_satoshis()
36 .ok_or(SparkSdkError::InvalidInput(
37 "Invoice missing amount".to_string(),
38 ))?;
39
40 let invoice_amount_sats = invoice_amount_msats / 1000;
41
42 if amount_sats == 0 || invoice_amount_sats == 0 {
44 return Err(SparkSdkError::InvalidInput(
45 "Amount must be greater than 0".to_string(),
46 ));
47 }
48
49 let payment_hash = bolt11_invoice.payment_hash().to_string();
51
52 let leaf_selection_response = self
54 .prepare_leaves_for_amount(amount_sats, None, LeafNodeStatus::InTransfer)
55 .await?;
56 let unlocking_id = leaf_selection_response.unlocking_id.clone().unwrap();
57 let leaves = leaf_selection_response.leaves;
58
59 let identity_pubkey = self.get_identity_public_key();
61 let network = self.get_network();
62 let mut leaf_tweaks = Vec::new();
63 for leaf in &leaves {
64 let tree_node = leaf.marshal_to_tree_node(identity_pubkey, &network);
65
66 let old_signing_private_key = self
67 .signer
68 .sensitive_expose_secret_key_from_pubkey(&leaf.signing_public_key, false)?;
69 let new_signing_public_key = self.signer.new_secp256k1_keypair(KeygenMethod::Random)?;
70
71 let leaf_tweak = LeafKeyTweak {
72 leaf: tree_node,
73 old_signing_private_key,
74 new_signing_public_key,
75 };
76 leaf_tweaks.push(leaf_tweak);
77 }
78
79 let payment_hash_bytes = hex::decode(&payment_hash).unwrap();
81 let swap_response = self
82 .swap_nodes_for_preimage(
83 leaf_tweaks.clone(),
84 identity_pubkey.to_vec(),
85 payment_hash_bytes,
86 Some(invoice),
87 0, false,
89 )
90 .await?;
91
92 println!("Swap response: {:?}", swap_response);
93
94 let transfer_data = swap_response.transfer.unwrap();
96 let transfer = self
97 .send_transfer_tweak_key(transfer_data, &leaf_tweaks, &HashMap::new())
98 .await?;
99
100 println!("Transfer: {:?}", transfer);
101
102 let lightning_send_response = self
104 .request_lightning_send_with_ssp(transfer.id, payment_hash)
105 .await?;
106
107 println!("Lightning send response: {:?}", lightning_send_response);
108
109 let leaf_ids_to_remove: Vec<String> = leaves.iter().map(|l| l.id.clone()).collect();
111 self.leaf_manager
112 .delete_leaves_after_transfer(&unlocking_id, &leaf_ids_to_remove)
113 .await?;
114
115 println!("Leaves deleted");
116
117 Ok(lightning_send_response)
118 }
119
120 pub async fn create_lightning_invoice(
121 &self,
122 amount_sats: i64,
123 memo: Option<String>,
124 expiry_seconds: Option<i32>,
125 ) -> Result<String, SparkSdkError> {
126 let preimage_sk = bitcoin::secp256k1::SecretKey::new(&mut OsRng);
129 let preimage_bytes = preimage_sk.secret_bytes();
130 let payment_hash = sha256::digest(&preimage_bytes);
131 let payment_hash_bytes = hex::decode(&payment_hash)?;
132
133 let (invoice, fees) = self
135 .create_invoice_with_ssp(
136 amount_sats,
137 payment_hash,
138 expiry_seconds,
139 memo,
140 self.config.spark_config.network.to_bitcoin_network(),
141 )
142 .await?;
143
144 let signing_operators = self.config.spark_config.spark_operators.clone();
146
147 let t = self.config.spark_config.threshold as usize;
150 let n = signing_operators.len();
151 let vss = VSS::new(t, n).unwrap();
152 let shares = vss
153 .split_from_secret_key(&preimage_sk)
154 .map_err(|e| SparkSdkError::CryptoError(format!("Failed to split preimage: {}", e)))?;
155
156 for operator in &signing_operators {
157 let operator_id = operator.id;
158 let share = &shares[operator_id as usize];
159
160 let mut operator_client = self
161 .config
162 .spark_config
163 .get_spark_connection(Some(operator_id))
164 .await?;
165
166 let mut request = tonic::Request::new(StorePreimageShareRequest {
167 payment_hash: payment_hash_bytes.clone(),
168 preimage_share: Some(share.marshal_proto()),
169 threshold: self.config.spark_config.threshold as u32,
170 invoice_string: invoice.clone(),
171 user_identity_public_key: self.get_identity_public_key().to_vec(),
172 });
173 self.add_authorization_header_to_request(&mut request, Some(operator_id));
174
175 operator_client.store_preimage_share(request).await?;
176 }
177
178 Ok(invoice)
179 }
180}