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 { leaf_index: usize },
120}
121
122impl TaprootManager {
123 pub fn new(config: TaprootConfig) -> Self {
125 Self {
126 config,
127 secp: Secp256k1::new(),
128 }
129 }
130
131 pub fn generate_keypair(&self) -> Result<TaprootKeyPair> {
133 let secret_key = SecretKey::new(&mut OsRng);
134 let public_key = PublicKey::from_secret_key(&self.secp, &secret_key);
135 let (x_only_pubkey, _parity) = public_key.x_only_public_key();
136
137 Ok(TaprootKeyPair {
138 internal_key: x_only_pubkey,
139 secret_key: Some(secret_key),
140 })
141 }
142
143 pub fn create_key_path_address(&self, internal_key: XOnlyPublicKey) -> Result<TaprootAddress> {
145 let builder = TaprootBuilder::new();
147 let spend_info = builder
148 .finalize(&self.secp, internal_key)
149 .map_err(|_| BitcoinError::Validation("Taproot finalization failed".to_string()))?;
150
151 let _output_key = spend_info.output_key();
152 let address = Address::p2tr(&self.secp, internal_key, None, self.config.network);
153
154 Ok(TaprootAddress {
155 address,
156 spend_info,
157 internal_key,
158 })
159 }
160
161 pub fn create_script_path_address(
163 &self,
164 internal_key: XOnlyPublicKey,
165 scripts: Vec<ScriptBuf>,
166 ) -> Result<TaprootAddress> {
167 if !self.config.enable_script_path {
168 return Err(BitcoinError::Validation(
169 "Script path spending is disabled".to_string(),
170 ));
171 }
172
173 if scripts.is_empty() {
174 return Err(BitcoinError::Validation(
175 "At least one script is required".to_string(),
176 ));
177 }
178
179 let mut builder = TaprootBuilder::new();
181 for script in scripts {
182 builder = builder
183 .add_leaf(0, script)
184 .map_err(|e| BitcoinError::Validation(format!("Failed to add leaf: {}", e)))?;
185 }
186
187 let spend_info = builder
188 .finalize(&self.secp, internal_key)
189 .map_err(|_| BitcoinError::Validation("Taproot finalization failed".to_string()))?;
190
191 let merkle_root = spend_info.merkle_root();
192 let address = Address::p2tr(&self.secp, internal_key, merkle_root, self.config.network);
193
194 Ok(TaprootAddress {
195 address,
196 spend_info,
197 internal_key,
198 })
199 }
200
201 pub fn validate_address(&self, address: &str) -> Result<bool> {
203 let addr = Address::from_str(address)
204 .map_err(|e| BitcoinError::InvalidAddress(format!("Invalid address: {}", e)))?
205 .require_network(self.config.network)
206 .map_err(|_| BitcoinError::InvalidAddress("Network mismatch".to_string()))?;
207
208 Ok(addr.script_pubkey().is_p2tr())
210 }
211
212 pub fn is_taproot_address(&self, address: &Address) -> bool {
214 address.script_pubkey().is_p2tr()
215 }
216
217 pub fn extract_internal_key(&self, address: &Address) -> Result<XOnlyPublicKey> {
219 if !address.script_pubkey().is_p2tr() {
220 return Err(BitcoinError::InvalidAddress(
221 "Address is not a Taproot address".to_string(),
222 ));
223 }
224
225 let script_pubkey = address.script_pubkey();
228 if script_pubkey.len() != 34 {
229 return Err(BitcoinError::InvalidAddress(
230 "Invalid Taproot script pubkey length".to_string(),
231 ));
232 }
233
234 let pubkey_bytes = &script_pubkey.as_bytes()[2..34];
236 XOnlyPublicKey::from_slice(pubkey_bytes)
237 .map_err(|e| BitcoinError::InvalidAddress(format!("Invalid x-only pubkey: {}", e)))
238 }
239}
240
241pub struct TaprootTxBuilder {
243 #[allow(dead_code)]
244 manager: Arc<TaprootManager>,
245}
246
247impl TaprootTxBuilder {
248 pub fn new(manager: Arc<TaprootManager>) -> Self {
250 Self { manager }
251 }
252
253 pub fn create_key_path_spend(
255 &self,
256 _prev_output: TxOut,
257 _destination: Address,
258 _amount: Amount,
259 ) -> Result<Transaction> {
260 Err(BitcoinError::Validation(
263 "Key path spending not yet implemented".to_string(),
264 ))
265 }
266
267 pub fn create_script_path_spend(
269 &self,
270 _prev_output: TxOut,
271 _destination: Address,
272 _amount: Amount,
273 _leaf_index: usize,
274 ) -> Result<Transaction> {
275 Err(BitcoinError::Validation(
278 "Script path spending not yet implemented".to_string(),
279 ))
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn test_taproot_config_defaults() {
289 let config = TaprootConfig::default();
290 assert_eq!(config.network, Network::Bitcoin);
291 assert!(config.enable_script_path);
292 assert_eq!(config.max_tree_depth, 128);
293 }
294
295 #[test]
296 fn test_generate_keypair() {
297 let config = TaprootConfig {
298 network: Network::Testnet,
299 ..Default::default()
300 };
301 let manager = TaprootManager::new(config);
302
303 let keypair = manager.generate_keypair().unwrap();
304 assert!(keypair.secret_key.is_some());
305 }
306
307 #[test]
308 fn test_create_key_path_address() {
309 let config = TaprootConfig {
310 network: Network::Testnet,
311 ..Default::default()
312 };
313 let manager = TaprootManager::new(config);
314
315 let keypair = manager.generate_keypair().unwrap();
316 let address = manager
317 .create_key_path_address(keypair.internal_key)
318 .unwrap();
319
320 assert!(manager.is_taproot_address(&address.address));
321 }
322
323 #[test]
324 fn test_create_script_path_address() {
325 let config = TaprootConfig {
326 network: Network::Testnet,
327 ..Default::default()
328 };
329 let manager = TaprootManager::new(config);
330
331 let keypair = manager.generate_keypair().unwrap();
332
333 let script = ScriptBuf::from_bytes(vec![0x51]); let address = manager
337 .create_script_path_address(keypair.internal_key, vec![script])
338 .unwrap();
339
340 assert!(manager.is_taproot_address(&address.address));
341 }
342
343 #[test]
344 fn test_validate_taproot_address() {
345 let config = TaprootConfig {
346 network: Network::Testnet,
347 ..Default::default()
348 };
349 let manager = TaprootManager::new(config);
350
351 let keypair = manager.generate_keypair().unwrap();
352 let address = manager
353 .create_key_path_address(keypair.internal_key)
354 .unwrap();
355
356 let is_valid = manager
357 .validate_address(&address.address.to_string())
358 .unwrap();
359 assert!(is_valid);
360 }
361
362 #[test]
363 fn test_script_path_disabled() {
364 let config = TaprootConfig {
365 network: Network::Testnet,
366 enable_script_path: false,
367 ..Default::default()
368 };
369 let manager = TaprootManager::new(config);
370
371 let keypair = manager.generate_keypair().unwrap();
372 let script = ScriptBuf::from_bytes(vec![0x51]);
373
374 let result = manager.create_script_path_address(keypair.internal_key, vec![script]);
375 assert!(result.is_err());
376 }
377}