Skip to main content

chains_sdk/bitcoin/
taproot.rs

1//! **BIP-341** — Taproot output construction and script-path spending.
2//!
3//! Implements the full TapTree (Merkelized Abstract Syntax Tree) for organizing
4//! spending conditions, key tweaking, control block construction, and P2TR
5//! address generation from script trees.
6//!
7//! # Example
8//! ```no_run
9//! use chains_sdk::bitcoin::taproot::{TapTree, TapLeaf};
10//!
11//! // Build a TapTree with two spending scripts
12//! let leaf1 = TapLeaf::new(0xC0, vec![0x51]); // OP_TRUE
13//! let leaf2 = TapLeaf::new(0xC0, vec![0x00]); // OP_FALSE
14//! let tree = TapTree::branch(TapTree::leaf(leaf1), TapTree::leaf(leaf2));
15//! let merkle_root = tree.merkle_root();
16//! ```
17
18use crate::crypto::tagged_hash;
19use crate::encoding;
20use crate::error::SignerError;
21
22// ─── TapLeaf ────────────────────────────────────────────────────────
23
24/// A leaf node in a TapTree, containing a script and leaf version.
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct TapLeaf {
27    /// The leaf version (default 0xC0 for Tapscript as defined in BIP-342).
28    pub version: u8,
29    /// The script bytes.
30    pub script: Vec<u8>,
31}
32
33impl TapLeaf {
34    /// Create a new TapLeaf with the given version and script.
35    pub fn new(version: u8, script: Vec<u8>) -> Self {
36        Self { version, script }
37    }
38
39    /// Create a Tapscript leaf (version 0xC0) with the given script.
40    pub fn tapscript(script: Vec<u8>) -> Self {
41        Self {
42            version: 0xC0,
43            script,
44        }
45    }
46
47    /// Compute the leaf hash: `tagged_hash("TapLeaf", version || compact_size(script) || script)`.
48    pub fn leaf_hash(&self) -> [u8; 32] {
49        let mut data = Vec::new();
50        data.push(self.version);
51        encoding::encode_compact_size(&mut data, self.script.len() as u64);
52        data.extend_from_slice(&self.script);
53        tagged_hash(b"TapLeaf", &data)
54    }
55}
56
57// ─── TapTree (Merkle Tree) ──────────────────────────────────────────
58
59/// A node in the Taproot Merkle tree.
60#[derive(Clone, Debug)]
61pub enum TapTree {
62    /// A leaf node containing a script.
63    Leaf(TapLeaf),
64    /// A branch node with two children.
65    Branch(Box<TapTree>, Box<TapTree>),
66}
67
68impl TapTree {
69    /// Create a leaf node.
70    pub fn leaf(tap_leaf: TapLeaf) -> Self {
71        TapTree::Leaf(tap_leaf)
72    }
73
74    /// Create a branch node from two children.
75    pub fn branch(left: TapTree, right: TapTree) -> Self {
76        TapTree::Branch(Box::new(left), Box::new(right))
77    }
78
79    /// Compute the merkle root hash of this tree node.
80    ///
81    /// - Leaf: `tagged_hash("TapLeaf", ...)`
82    /// - Branch: `tagged_hash("TapBranch", sort(left, right))`
83    pub fn merkle_root(&self) -> [u8; 32] {
84        match self {
85            TapTree::Leaf(leaf) => leaf.leaf_hash(),
86            TapTree::Branch(left, right) => {
87                let left_hash = left.merkle_root();
88                let right_hash = right.merkle_root();
89                tap_branch_hash(&left_hash, &right_hash)
90            }
91        }
92    }
93
94    /// Compute the merkle proof (path) for a specific leaf.
95    ///
96    /// Returns `Some(path)` where path is a list of 32-byte hashes,
97    /// or `None` if the leaf is not found in this tree.
98    pub fn merkle_proof(&self, target_leaf: &TapLeaf) -> Option<Vec<[u8; 32]>> {
99        self.find_proof(target_leaf)
100    }
101
102    fn find_proof(&self, target: &TapLeaf) -> Option<Vec<[u8; 32]>> {
103        match self {
104            TapTree::Leaf(leaf) => {
105                if leaf == target {
106                    Some(Vec::new())
107                } else {
108                    None
109                }
110            }
111            TapTree::Branch(left, right) => {
112                // Try left subtree
113                if let Some(mut path) = left.find_proof(target) {
114                    path.push(right.merkle_root());
115                    return Some(path);
116                }
117                // Try right subtree
118                if let Some(mut path) = right.find_proof(target) {
119                    path.push(left.merkle_root());
120                    return Some(path);
121                }
122                None
123            }
124        }
125    }
126
127    /// Count the total number of leaves in this tree.
128    pub fn leaf_count(&self) -> usize {
129        match self {
130            TapTree::Leaf(_) => 1,
131            TapTree::Branch(left, right) => left.leaf_count() + right.leaf_count(),
132        }
133    }
134
135    /// Get the depth of the tree.
136    pub fn depth(&self) -> usize {
137        match self {
138            TapTree::Leaf(_) => 0,
139            TapTree::Branch(left, right) => 1 + left.depth().max(right.depth()),
140        }
141    }
142}
143
144// ─── Branch Hashing ─────────────────────────────────────────────────
145
146/// Compute a TapBranch hash from two child hashes.
147///
148/// Per BIP-341: `tagged_hash("TapBranch", sort(a, b))` — the smaller hash comes first.
149pub fn tap_branch_hash(a: &[u8; 32], b: &[u8; 32]) -> [u8; 32] {
150    let mut data = Vec::with_capacity(64);
151    // Lexicographic sort
152    if a <= b {
153        data.extend_from_slice(a);
154        data.extend_from_slice(b);
155    } else {
156        data.extend_from_slice(b);
157        data.extend_from_slice(a);
158    }
159    tagged_hash(b"TapBranch", &data)
160}
161
162// ─── Key Tweaking ───────────────────────────────────────────────────
163
164/// Tweak an internal public key with a merkle root to produce the output key.
165///
166/// **BIP-341 Algorithm:**
167/// - `t = tagged_hash("TapTweak", internal_key || merkle_root)`
168/// - `Q = P + t*G`
169///
170/// Returns `Ok((tweaked_x_only_key, parity))` where parity indicates if Q has odd y.
171///
172/// If `merkle_root` is `None`, uses key-path-only: `t = tagged_hash("TapTweak", internal_key)`.
173///
174/// Returns `Err` if the internal key is not a valid curve point.
175pub fn taproot_tweak(
176    internal_key: &[u8; 32],
177    merkle_root: Option<&[u8; 32]>,
178) -> Result<([u8; 32], bool), SignerError> {
179    // Compute tweak scalar
180    let mut tweak_data = Vec::with_capacity(64);
181    tweak_data.extend_from_slice(internal_key);
182    if let Some(root) = merkle_root {
183        tweak_data.extend_from_slice(root);
184    }
185    let tweak = tagged_hash(b"TapTweak", &tweak_data);
186
187    // Parse internal key as x-only point (even y assumed)
188    use k256::elliptic_curve::ops::Reduce;
189    use k256::{ProjectivePoint, Scalar};
190
191    let mut pk_sec1 = [0u8; 33];
192    pk_sec1[0] = 0x02; // even y
193    pk_sec1[1..].copy_from_slice(internal_key);
194
195    // Parse the internal key point
196    #[allow(unused_imports)]
197    use k256::elliptic_curve::group::GroupEncoding;
198    use k256::AffinePoint;
199
200    let pk_ct = AffinePoint::from_bytes((&pk_sec1).into());
201    let pk_point: ProjectivePoint =
202        Option::from(pk_ct.map(ProjectivePoint::from)).ok_or_else(|| {
203            SignerError::InvalidPublicKey("taproot internal key is not a valid curve point".into())
204        })?;
205
206    // Compute t * G
207    let t_wide = k256::U256::from_be_slice(&tweak);
208    let t_scalar = <Scalar as Reduce<k256::U256>>::reduce(t_wide);
209    let tweaked = pk_point + ProjectivePoint::GENERATOR * t_scalar;
210
211    // Get the x-only output key
212    use k256::elliptic_curve::sec1::ToEncodedPoint;
213    let tweaked_affine = tweaked.to_affine();
214    let encoded = tweaked_affine.to_encoded_point(true);
215    let bytes = encoded.as_bytes();
216
217    let mut x_only = [0u8; 32];
218    x_only.copy_from_slice(&bytes[1..33]);
219
220    let parity = bytes[0] == 0x03;
221
222    Ok((x_only, parity))
223}
224
225/// Compute the full Taproot output key from an internal key and optional TapTree.
226///
227/// If `tree` is `None`, this produces a key-path-only P2TR output.
228pub fn taproot_output_key(
229    internal_key: &[u8; 32],
230    tree: Option<&TapTree>,
231) -> Result<([u8; 32], bool), SignerError> {
232    let merkle_root = tree.map(|t| t.merkle_root());
233    taproot_tweak(internal_key, merkle_root.as_ref())
234}
235
236// ─── Control Block ──────────────────────────────────────────────────
237
238/// A BIP-341 control block for script-path spending.
239#[derive(Clone, Debug)]
240pub struct ControlBlock {
241    /// The leaf version (high 7 bits) combined with parity bit (low bit).
242    pub leaf_version_and_parity: u8,
243    /// The 32-byte x-only internal public key.
244    pub internal_key: [u8; 32],
245    /// The merkle proof path (0 to 128 hashes of 32 bytes each).
246    pub merkle_path: Vec<[u8; 32]>,
247}
248
249impl ControlBlock {
250    /// Create a control block for a specific leaf in a tree.
251    ///
252    /// # Arguments
253    /// * `internal_key` - The 32-byte x-only internal public key
254    /// * `tree` - The TapTree
255    /// * `leaf` - The target leaf to create a proof for
256    /// * `output_key_parity` - Whether the output key Q has odd y
257    pub fn new(
258        internal_key: [u8; 32],
259        tree: &TapTree,
260        leaf: &TapLeaf,
261        output_key_parity: bool,
262    ) -> Option<Self> {
263        let merkle_path = tree.merkle_proof(leaf)?;
264        let parity_bit = if output_key_parity { 1u8 } else { 0u8 };
265        Some(ControlBlock {
266            leaf_version_and_parity: (leaf.version & 0xFE) | parity_bit,
267            internal_key,
268            merkle_path,
269        })
270    }
271
272    /// Serialize the control block to bytes.
273    ///
274    /// Format: `control_byte || internal_key (32) || merkle_path (32 * m)`
275    pub fn to_bytes(&self) -> Vec<u8> {
276        let mut bytes = Vec::with_capacity(33 + self.merkle_path.len() * 32);
277        bytes.push(self.leaf_version_and_parity);
278        bytes.extend_from_slice(&self.internal_key);
279        for hash in &self.merkle_path {
280            bytes.extend_from_slice(hash);
281        }
282        bytes
283    }
284
285    /// Parse a control block from bytes.
286    pub fn from_bytes(data: &[u8]) -> Option<Self> {
287        if data.len() < 33 || (data.len() - 33) % 32 != 0 {
288            return None;
289        }
290        let control_byte = data[0];
291        let mut internal_key = [0u8; 32];
292        internal_key.copy_from_slice(&data[1..33]);
293        let path_count = (data.len() - 33) / 32;
294        let mut merkle_path = Vec::with_capacity(path_count);
295        for i in 0..path_count {
296            let mut hash = [0u8; 32];
297            hash.copy_from_slice(&data[33 + i * 32..33 + (i + 1) * 32]);
298            merkle_path.push(hash);
299        }
300        Some(ControlBlock {
301            leaf_version_and_parity: control_byte,
302            internal_key,
303            merkle_path,
304        })
305    }
306
307    /// Verify the control block against a given output key and leaf.
308    ///
309    /// Reconstructs the merkle root from the proof path and verifies
310    /// that `taproot_tweak(internal_key, merkle_root) == output_key`.
311    pub fn verify(&self, output_key: &[u8; 32], leaf: &TapLeaf) -> bool {
312        // Start with the leaf hash
313        let mut current = leaf.leaf_hash();
314
315        // Walk the merkle path
316        for sibling in &self.merkle_path {
317            current = tap_branch_hash(&current, sibling);
318        }
319
320        // Verify the tweak
321        let Ok((tweaked, parity)) = taproot_tweak(&self.internal_key, Some(&current)) else {
322            return false;
323        };
324        let expected_parity = (self.leaf_version_and_parity & 1) == 1;
325
326        tweaked == *output_key && parity == expected_parity
327    }
328}
329
330// ─── P2TR Address Generation ────────────────────────────────────────
331
332/// Generate a P2TR (Taproot) Bech32m address from an internal key and optional tree.
333///
334/// # Arguments
335/// * `internal_key` - 32-byte x-only internal public key
336/// * `tree` - Optional TapTree for script-path spending
337/// * `hrp` - Human readable part ("bc" for mainnet, "tb" for testnet)
338pub fn taproot_address(
339    internal_key: &[u8; 32],
340    tree: Option<&TapTree>,
341    hrp: &str,
342) -> Result<String, SignerError> {
343    let (output_key, _parity) = taproot_output_key(internal_key, tree)?;
344    encoding::bech32_encode(hrp, 1, &output_key)
345}
346
347// ─── Tests ──────────────────────────────────────────────────────────
348
349#[cfg(test)]
350#[allow(clippy::unwrap_used, clippy::expect_used)]
351mod tests {
352    use super::*;
353
354    #[test]
355    fn test_tap_leaf_hash_deterministic() {
356        let leaf = TapLeaf::tapscript(vec![0x51]); // OP_TRUE
357        let h1 = leaf.leaf_hash();
358        let h2 = leaf.leaf_hash();
359        assert_eq!(h1, h2);
360    }
361
362    #[test]
363    fn test_tap_leaf_hash_different_scripts() {
364        let l1 = TapLeaf::tapscript(vec![0x51]); // OP_TRUE
365        let l2 = TapLeaf::tapscript(vec![0x00]); // OP_FALSE
366        assert_ne!(l1.leaf_hash(), l2.leaf_hash());
367    }
368
369    #[test]
370    fn test_tap_leaf_hash_different_versions() {
371        let l1 = TapLeaf::new(0xC0, vec![0x51]);
372        let l2 = TapLeaf::new(0xC2, vec![0x51]);
373        assert_ne!(l1.leaf_hash(), l2.leaf_hash());
374    }
375
376    #[test]
377    fn test_tap_branch_hash_commutative() {
378        let a = [0xAA; 32];
379        let b = [0xBB; 32];
380        // Branch hash should be the same regardless of order
381        assert_eq!(tap_branch_hash(&a, &b), tap_branch_hash(&b, &a));
382    }
383
384    #[test]
385    fn test_tap_tree_single_leaf() {
386        let leaf = TapLeaf::tapscript(vec![0x51]);
387        let tree = TapTree::leaf(leaf.clone());
388        assert_eq!(tree.leaf_count(), 1);
389        assert_eq!(tree.depth(), 0);
390        assert_eq!(tree.merkle_root(), leaf.leaf_hash());
391    }
392
393    #[test]
394    fn test_tap_tree_two_leaves() {
395        let l1 = TapLeaf::tapscript(vec![0x51]);
396        let l2 = TapLeaf::tapscript(vec![0x00]);
397        let tree = TapTree::branch(TapTree::leaf(l1.clone()), TapTree::leaf(l2.clone()));
398        assert_eq!(tree.leaf_count(), 2);
399        assert_eq!(tree.depth(), 1);
400        let expected = tap_branch_hash(&l1.leaf_hash(), &l2.leaf_hash());
401        assert_eq!(tree.merkle_root(), expected);
402    }
403
404    #[test]
405    fn test_tap_tree_three_leaves() {
406        let l1 = TapLeaf::tapscript(vec![0x51]);
407        let l2 = TapLeaf::tapscript(vec![0x00]);
408        let l3 = TapLeaf::tapscript(vec![0x52]); // OP_2
409        let tree = TapTree::branch(
410            TapTree::leaf(l1),
411            TapTree::branch(TapTree::leaf(l2), TapTree::leaf(l3)),
412        );
413        assert_eq!(tree.leaf_count(), 3);
414        assert_eq!(tree.depth(), 2);
415    }
416
417    #[test]
418    fn test_tap_tree_merkle_proof() {
419        let l1 = TapLeaf::tapscript(vec![0x51]);
420        let l2 = TapLeaf::tapscript(vec![0x00]);
421        let tree = TapTree::branch(TapTree::leaf(l1.clone()), TapTree::leaf(l2.clone()));
422
423        // Proof for l1 should contain l2's hash
424        let proof1 = tree.merkle_proof(&l1).expect("leaf found");
425        assert_eq!(proof1.len(), 1);
426        assert_eq!(proof1[0], l2.leaf_hash());
427
428        // Proof for l2 should contain l1's hash
429        let proof2 = tree.merkle_proof(&l2).expect("leaf found");
430        assert_eq!(proof2.len(), 1);
431        assert_eq!(proof2[0], l1.leaf_hash());
432    }
433
434    #[test]
435    fn test_tap_tree_merkle_proof_not_found() {
436        let l1 = TapLeaf::tapscript(vec![0x51]);
437        let l2 = TapLeaf::tapscript(vec![0x00]);
438        let unknown = TapLeaf::tapscript(vec![0xFF]);
439        let tree = TapTree::branch(TapTree::leaf(l1), TapTree::leaf(l2));
440        assert!(tree.merkle_proof(&unknown).is_none());
441    }
442
443    #[test]
444    fn test_taproot_tweak_key_path_only() {
445        // Key-path-only (no merkle root)
446        let internal_key = [0x01; 32];
447        let result = taproot_tweak(&internal_key, None);
448        // [0x01; 32] is not a valid x-coordinate on secp256k1
449        // Valid keys will return Ok, invalid will return Err
450        match result {
451            Ok((tweaked, _parity)) => {
452                assert_ne!(tweaked, internal_key); // tweaked key should differ
453                assert_ne!(tweaked, [0u8; 32]); // should not be zero
454
455                // Deterministic
456                let (tweaked2, _) = taproot_tweak(&internal_key, None).unwrap();
457                assert_eq!(tweaked, tweaked2);
458            }
459            Err(_) => {
460                // Invalid internal key — this is expected for arbitrary byte patterns
461            }
462        }
463    }
464
465    #[test]
466    fn test_taproot_tweak_with_merkle_root() {
467        // Use a known valid internal key (generator point x-coordinate)
468        let internal_key_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
469        let internal_key_bytes = hex::decode(internal_key_hex).unwrap();
470        let mut internal_key = [0u8; 32];
471        internal_key.copy_from_slice(&internal_key_bytes);
472
473        let merkle_root = [0xAA; 32];
474        let (tweaked1, _) = taproot_tweak(&internal_key, Some(&merkle_root)).unwrap();
475        let (tweaked2, _) = taproot_tweak(&internal_key, None).unwrap();
476        // Different merkle roots should produce different tweaked keys
477        assert_ne!(tweaked1, tweaked2);
478    }
479
480    #[test]
481    fn test_taproot_output_key_with_tree() {
482        let internal_key_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
483        let internal_key_bytes = hex::decode(internal_key_hex).unwrap();
484        let mut internal_key = [0u8; 32];
485        internal_key.copy_from_slice(&internal_key_bytes);
486        let l1 = TapLeaf::tapscript(vec![0x51]);
487        let tree = TapTree::leaf(l1);
488        let (output_key, _parity) = taproot_output_key(&internal_key, Some(&tree)).unwrap();
489        assert_ne!(output_key, [0u8; 32]);
490    }
491
492    #[test]
493    fn test_control_block_serialization() {
494        let l1 = TapLeaf::tapscript(vec![0x51]);
495        let l2 = TapLeaf::tapscript(vec![0x00]);
496        let tree = TapTree::branch(TapTree::leaf(l1.clone()), TapTree::leaf(l2.clone()));
497        let internal_key = [0x01; 32];
498        let (_, parity) = taproot_output_key(&internal_key, Some(&tree)).unwrap();
499
500        let cb = ControlBlock::new(internal_key, &tree, &l1, parity).expect("ok");
501        let bytes = cb.to_bytes();
502
503        // Size: 1 (control byte) + 32 (internal key) + 32 * 1 (one proof hash)
504        assert_eq!(bytes.len(), 65);
505
506        // Round-trip
507        let parsed = ControlBlock::from_bytes(&bytes).expect("valid");
508        assert_eq!(parsed.internal_key, internal_key);
509        assert_eq!(parsed.merkle_path.len(), 1);
510    }
511
512    #[test]
513    fn test_control_block_verify() {
514        let l1 = TapLeaf::tapscript(vec![0x51]);
515        let l2 = TapLeaf::tapscript(vec![0x00]);
516        let tree = TapTree::branch(TapTree::leaf(l1.clone()), TapTree::leaf(l2.clone()));
517
518        // Use a known valid internal key (generator point x-coordinate)
519        let internal_key_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
520        let internal_key_bytes = hex::decode(internal_key_hex).unwrap();
521        let mut internal_key = [0u8; 32];
522        internal_key.copy_from_slice(&internal_key_bytes);
523
524        let (output_key, parity) = taproot_output_key(&internal_key, Some(&tree)).unwrap();
525        let cb = ControlBlock::new(internal_key, &tree, &l1, parity).expect("ok");
526
527        assert!(cb.verify(&output_key, &l1));
528        // Verify with wrong leaf should fail
529        assert!(!cb.verify(&output_key, &l2));
530    }
531
532    #[test]
533    fn test_control_block_from_bytes_invalid() {
534        // Too short
535        assert!(ControlBlock::from_bytes(&[0x00; 10]).is_none());
536        // Wrong alignment
537        assert!(ControlBlock::from_bytes(&[0x00; 34]).is_none());
538        // Valid: 33 bytes (no proof)
539        assert!(ControlBlock::from_bytes(&[0x00; 33]).is_some());
540        // Valid: 65 bytes (1 proof hash)
541        assert!(ControlBlock::from_bytes(&[0x00; 65]).is_some());
542    }
543
544    #[test]
545    fn test_taproot_address_key_path_only() {
546        let internal_key_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
547        let internal_key_bytes = hex::decode(internal_key_hex).unwrap();
548        let mut internal_key = [0u8; 32];
549        internal_key.copy_from_slice(&internal_key_bytes);
550
551        let addr = taproot_address(&internal_key, None, "bc").expect("ok");
552        assert!(addr.starts_with("bc1p"));
553    }
554
555    #[test]
556    fn test_taproot_tweak_invalid_key() {
557        // All-zero key is not on the curve
558        let invalid_key = [0u8; 32];
559        let result = taproot_tweak(&invalid_key, None);
560        assert!(result.is_err(), "zeroed key should not be valid");
561    }
562
563    #[test]
564    fn test_taproot_address_with_tree() {
565        let internal_key_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
566        let internal_key_bytes = hex::decode(internal_key_hex).unwrap();
567        let mut internal_key = [0u8; 32];
568        internal_key.copy_from_slice(&internal_key_bytes);
569
570        let leaf = TapLeaf::tapscript(vec![0x51]);
571        let tree = TapTree::leaf(leaf);
572
573        let addr = taproot_address(&internal_key, Some(&tree), "bc").expect("ok");
574        assert!(addr.starts_with("bc1p"));
575
576        // Address with tree should differ from key-path-only
577        let addr_no_tree = taproot_address(&internal_key, None, "bc").expect("ok");
578        assert_ne!(addr, addr_no_tree);
579    }
580
581    #[test]
582    fn test_taproot_address_testnet() {
583        let internal_key_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
584        let internal_key_bytes = hex::decode(internal_key_hex).unwrap();
585        let mut internal_key = [0u8; 32];
586        internal_key.copy_from_slice(&internal_key_bytes);
587        let addr = taproot_address(&internal_key, None, "tb").expect("ok");
588        assert!(addr.starts_with("tb1p"));
589    }
590}