1use bitcoin::address::Address;
7use bitcoin::key::Secp256k1;
8use bitcoin::secp256k1::rand::rngs::OsRng;
9use bitcoin::secp256k1::{All, PublicKey, SecretKey, XOnlyPublicKey};
10use bitcoin::taproot::{TapLeafHash, TapNodeHash, TaprootBuilder, TaprootSpendInfo};
11use bitcoin::{Amount, Network, ScriptBuf, Transaction, TxOut};
12use serde::{Deserialize, Serialize};
13use std::str::FromStr;
14use std::sync::Arc;
15
16use crate::error::{BitcoinError, Result};
17
18#[derive(Debug, Clone)]
20pub struct TaprootConfig {
21 pub network: Network,
23 pub enable_script_path: bool,
25 pub max_tree_depth: u8,
27}
28
29impl Default for TaprootConfig {
30 fn default() -> Self {
31 Self {
32 network: Network::Bitcoin,
33 enable_script_path: true,
34 max_tree_depth: 128,
35 }
36 }
37}
38
39pub struct TaprootManager {
68 config: TaprootConfig,
69 secp: Secp256k1<All>,
70}
71
72#[derive(Debug, Clone)]
74pub struct TaprootKeyPair {
75 pub internal_key: XOnlyPublicKey,
77 #[allow(dead_code)]
79 secret_key: Option<SecretKey>,
80}
81
82#[derive(Debug, Clone)]
84pub struct TaprootScriptTree {
85 pub root: Option<TapNodeHash>,
87 pub leaves: Vec<TaprootScriptLeaf>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct TaprootScriptLeaf {
94 pub script: ScriptBuf,
96 pub version: u8,
98 pub leaf_hash: TapLeafHash,
100}
101
102#[derive(Debug, Clone)]
104pub struct TaprootAddress {
105 pub address: Address,
107 pub spend_info: TaprootSpendInfo,
109 pub internal_key: XOnlyPublicKey,
111}
112
113#[derive(Debug, Clone)]
115pub enum TaprootSpendPath {
116 KeyPath,
118 ScriptPath {
120 leaf_index: usize,
122 },
123}
124
125impl TaprootManager {
126 pub fn new(config: TaprootConfig) -> Self {
128 Self {
129 config,
130 secp: Secp256k1::new(),
131 }
132 }
133
134 pub fn generate_keypair(&self) -> Result<TaprootKeyPair> {
136 let secret_key = SecretKey::new(&mut OsRng);
137 let public_key = PublicKey::from_secret_key(&self.secp, &secret_key);
138 let (x_only_pubkey, _parity) = public_key.x_only_public_key();
139
140 Ok(TaprootKeyPair {
141 internal_key: x_only_pubkey,
142 secret_key: Some(secret_key),
143 })
144 }
145
146 pub fn create_key_path_address(&self, internal_key: XOnlyPublicKey) -> Result<TaprootAddress> {
148 let builder = TaprootBuilder::new();
150 let spend_info = builder
151 .finalize(&self.secp, internal_key)
152 .map_err(|_| BitcoinError::Validation("Taproot finalization failed".to_string()))?;
153
154 let _output_key = spend_info.output_key();
155 let address = Address::p2tr(&self.secp, internal_key, None, self.config.network);
156
157 Ok(TaprootAddress {
158 address,
159 spend_info,
160 internal_key,
161 })
162 }
163
164 pub fn create_script_path_address(
166 &self,
167 internal_key: XOnlyPublicKey,
168 scripts: Vec<ScriptBuf>,
169 ) -> Result<TaprootAddress> {
170 if !self.config.enable_script_path {
171 return Err(BitcoinError::Validation(
172 "Script path spending is disabled".to_string(),
173 ));
174 }
175
176 if scripts.is_empty() {
177 return Err(BitcoinError::Validation(
178 "At least one script is required".to_string(),
179 ));
180 }
181
182 let mut builder = TaprootBuilder::new();
184 for script in scripts {
185 builder = builder
186 .add_leaf(0, script)
187 .map_err(|e| BitcoinError::Validation(format!("Failed to add leaf: {}", e)))?;
188 }
189
190 let spend_info = builder
191 .finalize(&self.secp, internal_key)
192 .map_err(|_| BitcoinError::Validation("Taproot finalization failed".to_string()))?;
193
194 let merkle_root = spend_info.merkle_root();
195 let address = Address::p2tr(&self.secp, internal_key, merkle_root, self.config.network);
196
197 Ok(TaprootAddress {
198 address,
199 spend_info,
200 internal_key,
201 })
202 }
203
204 pub fn validate_address(&self, address: &str) -> Result<bool> {
206 let addr = Address::from_str(address)
207 .map_err(|e| BitcoinError::InvalidAddress(format!("Invalid address: {}", e)))?
208 .require_network(self.config.network)
209 .map_err(|_| BitcoinError::InvalidAddress("Network mismatch".to_string()))?;
210
211 Ok(addr.script_pubkey().is_p2tr())
213 }
214
215 pub fn is_taproot_address(&self, address: &Address) -> bool {
217 address.script_pubkey().is_p2tr()
218 }
219
220 pub fn extract_internal_key(&self, address: &Address) -> Result<XOnlyPublicKey> {
222 if !address.script_pubkey().is_p2tr() {
223 return Err(BitcoinError::InvalidAddress(
224 "Address is not a Taproot address".to_string(),
225 ));
226 }
227
228 let script_pubkey = address.script_pubkey();
231 if script_pubkey.len() != 34 {
232 return Err(BitcoinError::InvalidAddress(
233 "Invalid Taproot script pubkey length".to_string(),
234 ));
235 }
236
237 let pubkey_bytes = &script_pubkey.as_bytes()[2..34];
239 XOnlyPublicKey::from_slice(pubkey_bytes)
240 .map_err(|e| BitcoinError::InvalidAddress(format!("Invalid x-only pubkey: {}", e)))
241 }
242}
243
244pub struct TaprootTxBuilder {
246 #[allow(dead_code)]
247 manager: Arc<TaprootManager>,
248}
249
250impl TaprootTxBuilder {
251 pub fn new(manager: Arc<TaprootManager>) -> Self {
253 Self { manager }
254 }
255
256 pub fn create_key_path_spend(
258 &self,
259 _prev_output: TxOut,
260 _destination: Address,
261 _amount: Amount,
262 ) -> Result<Transaction> {
263 Err(BitcoinError::Validation(
266 "Key path spending not yet implemented".to_string(),
267 ))
268 }
269
270 pub fn create_script_path_spend(
272 &self,
273 _prev_output: TxOut,
274 _destination: Address,
275 _amount: Amount,
276 _leaf_index: usize,
277 ) -> Result<Transaction> {
278 Err(BitcoinError::Validation(
281 "Script path spending not yet implemented".to_string(),
282 ))
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn test_taproot_config_defaults() {
292 let config = TaprootConfig::default();
293 assert_eq!(config.network, Network::Bitcoin);
294 assert!(config.enable_script_path);
295 assert_eq!(config.max_tree_depth, 128);
296 }
297
298 #[test]
299 fn test_generate_keypair() {
300 let config = TaprootConfig {
301 network: Network::Testnet,
302 ..Default::default()
303 };
304 let manager = TaprootManager::new(config);
305
306 let keypair = manager.generate_keypair().unwrap();
307 assert!(keypair.secret_key.is_some());
308 }
309
310 #[test]
311 fn test_create_key_path_address() {
312 let config = TaprootConfig {
313 network: Network::Testnet,
314 ..Default::default()
315 };
316 let manager = TaprootManager::new(config);
317
318 let keypair = manager.generate_keypair().unwrap();
319 let address = manager
320 .create_key_path_address(keypair.internal_key)
321 .unwrap();
322
323 assert!(manager.is_taproot_address(&address.address));
324 }
325
326 #[test]
327 fn test_create_script_path_address() {
328 let config = TaprootConfig {
329 network: Network::Testnet,
330 ..Default::default()
331 };
332 let manager = TaprootManager::new(config);
333
334 let keypair = manager.generate_keypair().unwrap();
335
336 let script = ScriptBuf::from_bytes(vec![0x51]); let address = manager
340 .create_script_path_address(keypair.internal_key, vec![script])
341 .unwrap();
342
343 assert!(manager.is_taproot_address(&address.address));
344 }
345
346 #[test]
347 fn test_validate_taproot_address() {
348 let config = TaprootConfig {
349 network: Network::Testnet,
350 ..Default::default()
351 };
352 let manager = TaprootManager::new(config);
353
354 let keypair = manager.generate_keypair().unwrap();
355 let address = manager
356 .create_key_path_address(keypair.internal_key)
357 .unwrap();
358
359 let is_valid = manager
360 .validate_address(&address.address.to_string())
361 .unwrap();
362 assert!(is_valid);
363 }
364
365 #[test]
366 fn test_script_path_disabled() {
367 let config = TaprootConfig {
368 network: Network::Testnet,
369 enable_script_path: false,
370 ..Default::default()
371 };
372 let manager = TaprootManager::new(config);
373
374 let keypair = manager.generate_keypair().unwrap();
375 let script = ScriptBuf::from_bytes(vec![0x51]);
376
377 let result = manager.create_script_path_address(keypair.internal_key, vec![script]);
378 assert!(result.is_err());
379 }
380}