spark_sdk/wallet/internal_handlers/implementations/
lightning.rs1use crate::error::SparkSdkError;
2use crate::signer::traits::SparkSigner;
3use crate::wallet::internal_handlers::traits::lightning::LightningInternalHandlers;
4use crate::wallet::internal_handlers::traits::transfer::LeafKeyTweak;
5use crate::wallet::internal_handlers::utils::bitcoin_tx_from_bytes;
6use crate::wallet::internal_handlers::utils::parsers::parse_public_key;
7use crate::wallet::internal_handlers::utils::serialize_bitcoin_transaction;
8use crate::SparkSdk;
9use lightning_invoice::Bolt11Invoice;
10use spark_protos::common::SigningCommitment;
11use spark_protos::common::SigningResult;
12use spark_protos::spark::initiate_preimage_swap_request::Reason;
13use spark_protos::spark::GetSigningCommitmentsRequest;
14use spark_protos::spark::InitiatePreimageSwapRequest;
15use spark_protos::spark::InitiatePreimageSwapResponse;
16use spark_protos::spark::InvoiceAmountProof;
17use spark_protos::spark::RequestedSigningCommitments;
18use spark_protos::spark::SigningCommitments;
19use spark_protos::spark::UserSignedRefund;
20use std::collections::HashMap;
21use tonic::async_trait;
22use uuid::Uuid;
23
24#[async_trait]
25impl<S: SparkSigner + Send + Sync + Clone + 'static> LightningInternalHandlers<S> for SparkSdk<S> {
26 async fn swap_nodes_for_preimage(
27 &self,
28 leaves: Vec<LeafKeyTweak>,
29 receiver_identity_pubkey_bytes: Vec<u8>,
30 payment_hash: Vec<u8>,
31 invoice_string: Option<String>,
32 fee_sats: u64,
33 is_inbound_payment: bool,
34 ) -> Result<InitiatePreimageSwapResponse, SparkSdkError> {
35 let transfer_id = Uuid::now_v7();
36
37 let mut node_ids = Vec::with_capacity(leaves.len());
39 for leaf in &leaves {
40 node_ids.push(leaf.leaf.id.clone());
41 }
42
43 let mut spark_client = self.config.spark_config.get_spark_connection(None).await?;
44 let mut request = tonic::Request::new(GetSigningCommitmentsRequest { node_ids });
45 self.add_authorization_header_to_request(&mut request, None);
46 let response = spark_client.get_signing_commitments(request).await?;
47 let spark_signing_commitments = response.into_inner().signing_commitments;
48
49 println!("Signing commitmente: {:?}", spark_signing_commitments);
50
51 let receiver_identity_pubkey = parse_public_key(&receiver_identity_pubkey_bytes)?;
53 let (signing_result, refund_txs, user_commitments) = self.signer.sign_for_lightning_swap(
54 &leaves,
55 &spark_signing_commitments,
56 receiver_identity_pubkey,
57 )?;
58
59 let user_signed_refunds = prepare_user_signed_refunds(
61 &leaves,
62 &refund_txs,
63 &signing_result.results,
64 &user_commitments,
65 &spark_signing_commitments,
66 )?;
67
68 let mut bolt11_string = "".to_string(); let amount_sats = if let Some(invoice_str) = &invoice_string {
70 bolt11_string = invoice_str.clone();
71 let invoice: Bolt11Invoice = bolt11_string.parse().unwrap();
72 invoice
73 .amount_milli_satoshis()
74 .ok_or(SparkSdkError::InvalidInput(
75 "Invoice missing amount".to_string(),
76 ))?
77 / 1000
78 } else {
79 0 };
81
82 let reason = if is_inbound_payment {
83 Reason::Receive
84 } else {
85 Reason::Send
86 };
87
88 println!("User signed refunds: {:?}", user_signed_refunds);
89 for refund in &user_signed_refunds {
91 let refund_tx = bitcoin_tx_from_bytes(&refund.refund_tx)?;
93 println!(
94 "Refund tx is valid. It is: {:?}",
95 hex::encode(serialize_bitcoin_transaction(&refund_tx)?)
96 );
97 }
98
99 let mut request = tonic::Request::new(InitiatePreimageSwapRequest {
100 payment_hash: payment_hash.clone(),
101 user_signed_refunds,
102 reason: reason as i32,
103 invoice_amount: Some(spark_protos::spark::InvoiceAmount {
104 invoice_amount_proof: Some(InvoiceAmountProof {
105 bolt11_invoice: bolt11_string,
106 }),
107 value_sats: amount_sats,
108 }),
109 transfer: Some(spark_protos::spark::StartSendTransferRequest {
110 transfer_id: transfer_id.to_string(),
111 owner_identity_public_key: self.get_identity_public_key().to_vec(),
112 receiver_identity_public_key: receiver_identity_pubkey_bytes.clone(),
113 leaves_to_send: Default::default(),
114 expiry_time: None,
115 }),
116 receiver_identity_public_key: receiver_identity_pubkey_bytes,
117 fee_sats,
118 });
119 self.add_authorization_header_to_request(&mut request, None);
120
121 let response = spark_client.initiate_preimage_swap(request).await?;
122
123 Ok(response.into_inner())
124 }
125}
126
127fn prepare_user_signed_refunds(
128 leaves: &Vec<LeafKeyTweak>,
129 refund_txs: &Vec<Vec<u8>>,
130 signing_results: &HashMap<String, SigningResult>,
131 user_commitments: &Vec<SigningCommitment>,
132 signing_commitments: &Vec<RequestedSigningCommitments>,
133) -> Result<Vec<UserSignedRefund>, SparkSdkError> {
134 let mut user_signed_refunds = Vec::new();
135
136 for (i, leaf) in leaves.iter().enumerate() {
137 user_signed_refunds.push(UserSignedRefund {
141 node_id: leaf.leaf.id.clone(),
142 refund_tx: refund_txs[i].clone(),
143 user_signature: signing_results
144 .get(&leaf.leaf.id)
145 .ok_or(SparkSdkError::InvalidInput(
146 "Missing signing result".to_string(),
147 ))?
148 .signature_share
149 .clone(),
150 signing_commitments: Some(SigningCommitments {
151 signing_commitments: signing_commitments[i].signing_nonce_commitments.clone(),
152 }),
153 user_signature_commitment: Some(user_commitments[i].clone()),
154 });
155 }
156
157 Ok(user_signed_refunds)
158}