spark_rust/wallet/handlers/
swap.rs1use crate::constants::spark::DEFAULT_TRANSFER_EXPIRY;
2use crate::error::{IoError, NetworkError, SparkSdkError};
3use crate::signer::traits::derivation_path::SparkKeyType;
4use crate::signer::traits::SparkSigner;
5use crate::wallet::internal_handlers::traits::ssp::SspInternalHandlers;
6use crate::wallet::internal_handlers::traits::ssp::SwapLeaf;
7use crate::wallet::internal_handlers::traits::transfer::LeafKeyTweak;
8use crate::wallet::internal_handlers::traits::transfer::TransferInternalHandlers;
9use crate::wallet::leaf_manager::SparkNodeStatus;
10use crate::wallet::utils::bitcoin::{bitcoin_tx_from_bytes, parse_public_key, sighash_from_tx};
11use crate::wallet::utils::bitcoin::{
12 compute_taproot_key_no_script_from_internal_key, parse_secret_key,
13};
14use crate::SparkSdk;
15use bitcoin::key::Secp256k1;
16use spark_cryptography::adaptor_signature::apply_adaptor_to_signature;
17use spark_cryptography::adaptor_signature::generate_adaptor_from_signature;
18use spark_cryptography::adaptor_signature::generate_signature_from_existing_adaptor;
19use spark_protos::spark::query_nodes_request::Source;
20use spark_protos::spark::QueryNodesRequest;
21use spark_protos::spark::TreeNodeIds;
22
23impl<S: SparkSigner + Send + Sync + Clone + 'static> SparkSdk<S> {
24 #[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
25 pub async fn request_leaves_swap(&self, target_amount: u64) -> Result<String, SparkSdkError> {
26 let available_leaves = self
29 .leaf_manager
30 .lock_available_bitcoin_leaves(SparkNodeStatus::Swap);
31
32 let mut leaf_key_tweaks = Vec::with_capacity(available_leaves.leaves.len());
34
35 for leaf in available_leaves.leaves.iter() {
36 let tree_leaf = leaf.get_tree_node()?;
37
38 let old_signing_private_key = self.signer.expose_leaf_secret_key_for_transfer(
39 leaf.get_id().clone(),
40 SparkKeyType::BaseSigning,
41 0,
42 self.config.spark_config.network.to_bitcoin_network(),
43 )?;
44
45 let new_signing_public_key = self.signer.new_ephemeral_keypair()?;
47
48 leaf_key_tweaks.push(LeafKeyTweak {
49 leaf: tree_leaf,
50 old_signing_private_key,
51 new_signing_public_key,
52 });
53 }
54
55 let expiry_time = chrono::Utc::now().timestamp() as u64 + DEFAULT_TRANSFER_EXPIRY;
56
57 let (transfer, refund_signature_map) = self
58 .send_transfer_sign_refunds(
59 &leaf_key_tweaks,
60 &self.config.spark_config.ssp_identity_public_key,
61 expiry_time,
62 )
63 .await?;
64
65 let leaf = transfer.leaves[0].leaf.as_ref().unwrap();
66 let leaf_refund_signature = refund_signature_map[&leaf.id].clone();
67
68 let adaptor_signature =
69 generate_adaptor_from_signature(leaf_refund_signature.as_slice().try_into().unwrap())
70 .unwrap();
71
72 let mut user_leaves = Vec::new();
73 user_leaves.push(SwapLeaf {
74 leaf_id: transfer.leaves[0].leaf.as_ref().unwrap().id.clone(),
75 raw_unsigned_refund_transaction: hex::encode(
76 &transfer.leaves[0].intermediate_refund_tx,
77 ),
78 adaptor_added_signature: hex::encode(adaptor_signature.signature),
79 });
80
81 for leaf in transfer.leaves.iter().skip(1) {
82 let leaf_id = leaf.leaf.as_ref().unwrap().id.clone();
83 let leaf_refund_signature = refund_signature_map[&leaf_id].clone();
84
85 let signature = generate_signature_from_existing_adaptor(
86 &leaf_refund_signature,
87 adaptor_signature.adaptor_private_key.as_slice(),
88 )
89 .unwrap();
90
91 user_leaves.push(SwapLeaf {
92 leaf_id,
93 raw_unsigned_refund_transaction: hex::encode(&leaf.intermediate_refund_tx),
94 adaptor_added_signature: hex::encode(signature),
95 });
96 }
97
98 let total_amount = leaf_key_tweaks
100 .iter()
101 .map(|leaf| leaf.leaf.value)
102 .sum::<u64>();
103
104 let secp = Secp256k1::new();
105 let adaptor_private_key =
106 parse_secret_key(&adaptor_signature.adaptor_private_key.to_vec())?;
107 let adaptor_public_key = adaptor_private_key.public_key(&secp);
108
109 let (request_id, leaves) = self
110 .request_swap_leaves_with_ssp(
111 hex::encode(adaptor_public_key.serialize()),
112 total_amount,
113 target_amount,
114 0,
115 user_leaves,
116 )
117 .await?;
118
119 let mut spark_client = self.config.spark_config.get_spark_connection(None).await?;
120 let network = self.config.spark_config.network;
121
122 for leaf in &leaves {
123 let leaf_id = leaf.leaf_id.clone();
124 let response = spark_client
125 .query_nodes(QueryNodesRequest {
126 source: Some(Source::NodeIds(TreeNodeIds {
127 node_ids: vec![leaf_id.clone()],
128 })),
129 include_parents: Default::default(),
130 network: network.marshal_proto(),
131 })
132 .await
133 .map_err(|status| SparkSdkError::from(NetworkError::Status(status)))?
134 .into_inner();
135
136 let node = response
137 .nodes
138 .get(&leaf_id)
139 .ok_or(SparkSdkError::from(NetworkError::InvalidResponse))?;
140
141 #[cfg(feature = "telemetry")]
142 tracing::trace!(
143 leaf_id = leaf_id.clone(),
144 "Leaf balance returned for SSP swap: {:?}",
145 node.value
146 );
147
148 let node_tx = bitcoin_tx_from_bytes(&node.node_tx)?;
149 let refund_tx_bytes = hex::decode(&leaf.raw_unsigned_refund_transaction)
150 .map_err(|err| SparkSdkError::from(IoError::Decoding(err)))?;
151 let refund_tx = bitcoin_tx_from_bytes(&refund_tx_bytes)?;
152
153 let sighash = sighash_from_tx(&refund_tx, 0, &node_tx.output[0])?;
154
155 let verifying_public_key = parse_public_key(&node.verifying_public_key)?;
157
158 let taproot_key = compute_taproot_key_no_script_from_internal_key(
159 &verifying_public_key.x_only_public_key().0.serialize(),
160 )?;
161
162 let adaptor_signature_bytes = hex::decode(&leaf.adaptor_added_signature)
163 .map_err(|err| SparkSdkError::from(IoError::Decoding(err)))?;
164
165 let _ = apply_adaptor_to_signature(
166 &taproot_key,
167 &sighash,
168 &adaptor_signature_bytes,
169 &adaptor_private_key.secret_bytes(),
170 )
171 .unwrap();
172 }
173
174 let transfer_id = transfer.id.clone();
175 self.send_transfer_tweak_key(transfer.clone(), &leaf_key_tweaks, &refund_signature_map)
176 .await?;
177
178 let _completion = self
180 .complete_leaves_swap_with_ssp(
181 hex::encode(adaptor_private_key.secret_bytes()),
182 transfer_id,
183 request_id.clone(),
184 )
185 .await?;
186
187 self.claim_transfers().await?;
188
189 let leaf_ids = available_leaves
191 .leaves
192 .iter()
193 .map(|leaf| leaf.get_id().clone())
194 .collect();
195 self.leaf_manager
196 .unlock_leaves(available_leaves.unlocking_id.unwrap(), &leaf_ids, true)?;
197
198 let leaf_id = leaves[0].leaf_id.clone();
200
201 Ok(leaf_id)
202 }
203}