rustywallet_taproot/
control_block.rs

1//! Control block for Taproot script path spending
2//!
3//! The control block proves that a script is part of the tap tree.
4
5use crate::error::TaprootError;
6use crate::tagged_hash::TapNodeHash;
7use crate::taptree::{LeafVersion, TapLeaf, TapTree};
8use crate::xonly::{Parity, XOnlyPublicKey};
9
10/// Control block for script path spending
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct ControlBlock {
13    /// Leaf version and output key parity (combined in first byte)
14    pub leaf_version: LeafVersion,
15    /// Output key parity
16    pub output_key_parity: Parity,
17    /// Internal key (32 bytes)
18    pub internal_key: XOnlyPublicKey,
19    /// Merkle path (32-byte hashes)
20    pub merkle_path: Vec<TapNodeHash>,
21}
22
23impl ControlBlock {
24    /// Create a new control block
25    pub fn new(
26        leaf_version: LeafVersion,
27        output_key_parity: Parity,
28        internal_key: XOnlyPublicKey,
29        merkle_path: Vec<TapNodeHash>,
30    ) -> Self {
31        Self {
32            leaf_version,
33            output_key_parity,
34            internal_key,
35            merkle_path,
36        }
37    }
38
39    /// Create control block for a leaf in a tree
40    pub fn for_leaf(
41        tree: &TapTree,
42        leaf: &TapLeaf,
43        internal_key: XOnlyPublicKey,
44        output_key_parity: Parity,
45    ) -> Result<Self, TaprootError> {
46        let merkle_path = tree
47            .merkle_path(leaf)
48            .ok_or(TaprootError::InvalidMerklePath)?;
49
50        Ok(Self {
51            leaf_version: leaf.version,
52            output_key_parity,
53            internal_key,
54            merkle_path,
55        })
56    }
57
58    /// Serialize to bytes
59    pub fn serialize(&self) -> Vec<u8> {
60        let mut bytes = Vec::with_capacity(33 + self.merkle_path.len() * 32);
61
62        // First byte: leaf version | parity bit
63        let first_byte = self.leaf_version.0 | self.output_key_parity.to_u8();
64        bytes.push(first_byte);
65
66        // Internal key (32 bytes)
67        bytes.extend_from_slice(&self.internal_key.serialize());
68
69        // Merkle path
70        for hash in &self.merkle_path {
71            bytes.extend_from_slice(hash.as_bytes());
72        }
73
74        bytes
75    }
76
77    /// Parse from bytes
78    pub fn from_slice(data: &[u8]) -> Result<Self, TaprootError> {
79        if data.len() < 33 {
80            return Err(TaprootError::InvalidControlBlock(
81                "Control block too short".into(),
82            ));
83        }
84
85        if !(data.len() - 33).is_multiple_of(32) {
86            return Err(TaprootError::InvalidControlBlock(
87                "Invalid merkle path length".into(),
88            ));
89        }
90
91        let first_byte = data[0];
92        let leaf_version = LeafVersion(first_byte & 0xfe);
93        let output_key_parity = Parity::from_u8(first_byte & 0x01)?;
94
95        let internal_key = XOnlyPublicKey::from_slice(&data[1..33])?;
96
97        let path_count = (data.len() - 33) / 32;
98        let mut merkle_path = Vec::with_capacity(path_count);
99        for i in 0..path_count {
100            let start = 33 + i * 32;
101            let mut hash = [0u8; 32];
102            hash.copy_from_slice(&data[start..start + 32]);
103            merkle_path.push(TapNodeHash::from_bytes(hash));
104        }
105
106        Ok(Self {
107            leaf_version,
108            output_key_parity,
109            internal_key,
110            merkle_path,
111        })
112    }
113
114    /// Verify the control block against an output key and script
115    pub fn verify(
116        &self,
117        output_key: &XOnlyPublicKey,
118        script: &[u8],
119    ) -> Result<bool, TaprootError> {
120        use crate::tweak::tweak_public_key;
121
122        // Compute leaf hash
123        let leaf_hash = crate::tagged_hash::TapLeafHash::from_script(
124            self.leaf_version.0,
125            script,
126        );
127
128        // Compute merkle root by walking up the path
129        let mut current = TapNodeHash::from_leaf(leaf_hash);
130        for sibling in &self.merkle_path {
131            current = TapNodeHash::from_children(&current, sibling);
132        }
133
134        // Compute expected output key
135        let (expected_output, expected_parity) = tweak_public_key(
136            &self.internal_key,
137            Some(&current),
138        )?;
139
140        // Verify
141        Ok(expected_output == *output_key && expected_parity == self.output_key_parity)
142    }
143
144    /// Get the size in bytes
145    pub fn size(&self) -> usize {
146        33 + self.merkle_path.len() * 32
147    }
148
149    /// Get the merkle path depth
150    pub fn depth(&self) -> usize {
151        self.merkle_path.len()
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use crate::taptree::two_leaf_tree;
159    use crate::tweak::tweak_public_key;
160    use secp256k1::{Secp256k1, SecretKey};
161
162    fn get_test_internal_key() -> XOnlyPublicKey {
163        let secp = Secp256k1::new();
164        let secret = [
165            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
169        ];
170        let sk = SecretKey::from_slice(&secret).unwrap();
171        let pk = sk.public_key(&secp);
172        let (xonly, _) = pk.x_only_public_key();
173        XOnlyPublicKey::from_inner(xonly)
174    }
175
176    #[test]
177    fn test_control_block_roundtrip() {
178        let internal_key = get_test_internal_key();
179        let merkle_path = vec![TapNodeHash::from_bytes([0x42; 32])];
180
181        let cb = ControlBlock::new(
182            LeafVersion::TAPSCRIPT,
183            Parity::Even,
184            internal_key,
185            merkle_path,
186        );
187
188        let bytes = cb.serialize();
189        let parsed = ControlBlock::from_slice(&bytes).unwrap();
190
191        assert_eq!(cb.leaf_version, parsed.leaf_version);
192        assert_eq!(cb.output_key_parity, parsed.output_key_parity);
193        assert_eq!(cb.internal_key, parsed.internal_key);
194        assert_eq!(cb.merkle_path, parsed.merkle_path);
195    }
196
197    #[test]
198    fn test_control_block_size() {
199        let internal_key = get_test_internal_key();
200
201        // No merkle path
202        let cb = ControlBlock::new(
203            LeafVersion::TAPSCRIPT,
204            Parity::Even,
205            internal_key,
206            vec![],
207        );
208        assert_eq!(cb.size(), 33);
209
210        // One element in path
211        let cb = ControlBlock::new(
212            LeafVersion::TAPSCRIPT,
213            Parity::Even,
214            internal_key,
215            vec![TapNodeHash::from_bytes([0; 32])],
216        );
217        assert_eq!(cb.size(), 65);
218    }
219
220    #[test]
221    fn test_control_block_verify() {
222        let internal_key = get_test_internal_key();
223        let script1 = vec![0x51]; // OP_1
224        let script2 = vec![0x52]; // OP_2
225
226        let tree = two_leaf_tree(script1.clone(), script2);
227        let merkle_root = tree.root_hash();
228
229        let (output_key, parity) = tweak_public_key(&internal_key, Some(&merkle_root)).unwrap();
230
231        let leaf = crate::taptree::TapLeaf::new(script1.clone());
232        let cb = ControlBlock::for_leaf(&tree, &leaf, internal_key, parity).unwrap();
233
234        assert!(cb.verify(&output_key, &script1).unwrap());
235    }
236
237    #[test]
238    fn test_first_byte_encoding() {
239        let internal_key = get_test_internal_key();
240
241        // Even parity
242        let cb = ControlBlock::new(
243            LeafVersion::TAPSCRIPT,
244            Parity::Even,
245            internal_key,
246            vec![],
247        );
248        let bytes = cb.serialize();
249        assert_eq!(bytes[0], 0xc0); // 0xc0 | 0 = 0xc0
250
251        // Odd parity
252        let cb = ControlBlock::new(
253            LeafVersion::TAPSCRIPT,
254            Parity::Odd,
255            internal_key,
256            vec![],
257        );
258        let bytes = cb.serialize();
259        assert_eq!(bytes[0], 0xc1); // 0xc0 | 1 = 0xc1
260    }
261}