bitcoin_bosd/
address.rs

1//! # Address to Descriptor safe conversions.
2//!
3//! This module implements conversions between [`Address`] and [`Descriptor`].
4//!
5//! Note you need to use the `address` feature.
6
7use bitcoin::{
8    address::AddressData,
9    hashes::{hash160::Hash as Hash160, sha256::Hash as Hash256, Hash as _},
10    key::{TapTweak, TweakedPublicKey},
11    script::PushBytes,
12    Address, Network, PubkeyHash, ScriptBuf, ScriptHash, WPubkeyHash, WScriptHash, WitnessProgram,
13    WitnessVersion, XOnlyPublicKey,
14};
15
16use crate::{
17    descriptor::{
18        P2A_PROGRAM_BYTES, P2PKH_LEN, P2SH_LEN, P2TR_LEN, P2TR_TYPE_TAG, P2WPKH_LEN,
19        P2WPKH_P2WSH_TYPE_TAG, P2WSH_LEN,
20    },
21    Descriptor, DescriptorError,
22    DescriptorType::*,
23};
24
25impl Descriptor {
26    /// Converts the [`Descriptor`] to a Bitcoin [`Address`], given a [`Network`].
27    pub fn to_address(&self, network: Network) -> Result<Address, DescriptorError> {
28        match self.type_tag() {
29            OpReturn => Err(DescriptorError::InvalidAddressConversion(OpReturn)),
30            P2pkh => {
31                let bytes = self.to_fixed_payload_bytes::<P2PKH_LEN>();
32                let hash = Hash160::from_bytes_ref(&bytes);
33                let address = Address::p2pkh(*hash, network);
34                Ok(address)
35            }
36            P2sh => {
37                let bytes = self.to_fixed_payload_bytes::<P2SH_LEN>();
38                let hash = Hash160::from_bytes_ref(&bytes);
39                let script_hash = ScriptHash::from_raw_hash(*hash);
40                let address = Address::p2sh_from_hash(script_hash, network);
41                Ok(address)
42            }
43            P2wpkh => {
44                let bytes = self.to_fixed_payload_bytes::<P2WPKH_LEN>();
45                // V0 is SegWit 20-byte P2WPKH
46                let witness_program = WitnessProgram::new(WitnessVersion::V0, &bytes)?;
47                let address = Address::from_witness_program(witness_program, network);
48                Ok(address)
49            }
50            P2wsh => {
51                let bytes = self.to_fixed_payload_bytes::<P2WSH_LEN>();
52                // V0 is SegWit 32-byte P2WSH
53                let witness_program = WitnessProgram::new(WitnessVersion::V0, &bytes)?;
54                let address = Address::from_witness_program(witness_program, network);
55                Ok(address)
56            }
57            P2a => {
58                // V1 is SegWit 0-byte P2A
59                let witness_program = WitnessProgram::p2a();
60                let address = Address::from_witness_program(witness_program, network);
61                Ok(address)
62            }
63            P2tr => {
64                let bytes = self.to_fixed_payload_bytes::<P2TR_LEN>();
65                // V1 is SegWit 32-byte P2TR
66                let xonly_pubkey = XOnlyPublicKey::from_slice(&bytes)?;
67                // WARN: we are assuming that the X-only public key is already tweaked
68                //       and not the internal key.
69                //       See [BIP 341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
70                //       for more details.
71                let address =
72                    Address::p2tr_tweaked(xonly_pubkey.dangerous_assume_tweaked(), network);
73                Ok(address)
74            }
75        }
76    }
77
78    /// Converts the [`Descriptor`] to a Bitcoin [`ScriptBuf`].
79    ///
80    /// This is a standard relay-safe ScriptPubKey for a Bitcoin output.
81    pub fn to_script(&self) -> ScriptBuf {
82        let type_tag = self.type_tag();
83        match type_tag {
84            OpReturn => {
85                let payload = self.payload();
86                let push_bytes = <&PushBytes>::try_from(payload)
87                    .expect("payload length validated on construction");
88                let script = ScriptBuf::new_op_return(push_bytes);
89                assert!(script.is_op_return());
90                script
91            }
92            P2pkh => {
93                let bytes = self.to_fixed_payload_bytes::<P2PKH_LEN>();
94                let hash = Hash160::from_bytes_ref(&bytes);
95                let pubkey_hash = PubkeyHash::from_raw_hash(*hash);
96                ScriptBuf::new_p2pkh(&pubkey_hash)
97            }
98            P2sh => {
99                let bytes = self.to_fixed_payload_bytes::<P2SH_LEN>();
100                let hash = Hash160::from_bytes_ref(&bytes);
101                let script_hash = ScriptHash::from_raw_hash(*hash);
102                ScriptBuf::new_p2sh(&script_hash)
103            }
104            P2wpkh => {
105                let bytes = self.to_fixed_payload_bytes::<P2WPKH_LEN>();
106                let hash = Hash160::from_bytes_ref(&bytes);
107                let wpubkey_hash = WPubkeyHash::from_raw_hash(*hash);
108                ScriptBuf::new_p2wpkh(&wpubkey_hash)
109            }
110            P2wsh => {
111                let bytes = self.to_fixed_payload_bytes::<P2WSH_LEN>();
112                let hash = Hash256::from_bytes_ref(&bytes);
113                let wscript_hash = WScriptHash::from_raw_hash(*hash);
114                ScriptBuf::new_p2wsh(&wscript_hash)
115            }
116            P2a => ScriptBuf::new_p2a(),
117            P2tr => {
118                let bytes = self.to_fixed_payload_bytes::<P2TR_LEN>();
119                // WARN: we are assuming that the X-only public key is already tweaked
120                //       and not the internal key.
121                //       See [BIP 341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
122                //       for more details.
123                let xonly_pubkey = XOnlyPublicKey::from_slice(&bytes).expect("infallible");
124                ScriptBuf::new_p2tr_tweaked(xonly_pubkey.dangerous_assume_tweaked())
125            }
126        }
127    }
128}
129
130impl TryFrom<Address> for Descriptor {
131    type Error = DescriptorError;
132
133    fn try_from(value: Address) -> Result<Self, Self::Error> {
134        let address_data = value.to_address_data();
135        match address_data {
136            // P2PKH
137            AddressData::P2pkh { pubkey_hash } => Ok(Descriptor::new_p2pkh(
138                &pubkey_hash.as_raw_hash().to_byte_array(),
139            )),
140            // P2SH
141            AddressData::P2sh { script_hash } => Ok(Descriptor::new_p2sh(
142                &script_hash.as_raw_hash().to_byte_array(),
143            )),
144            // SegWit V0/V1
145            AddressData::Segwit { witness_program } => match witness_program.version() {
146                WitnessVersion::V0 => {
147                    let payload = witness_program.program().as_bytes();
148                    let payload_len = payload.len();
149                    match payload_len {
150                        // P2WPKH: 20 bytes
151                        20 => {
152                            let mut bytes = [0u8; 21];
153                            bytes[0] = P2WPKH_P2WSH_TYPE_TAG;
154                            bytes[1..].copy_from_slice(payload);
155                            Descriptor::from_bytes(&bytes)
156                        }
157                        // P2WSH: 32 bytes
158                        32 => {
159                            let mut bytes = [0u8; 33];
160                            bytes[0] = P2WPKH_P2WSH_TYPE_TAG;
161                            bytes[1..].copy_from_slice(payload);
162                            Descriptor::from_bytes(&bytes)
163                        }
164                        // Non-standard v0 witness program length (consensus-valid per BIP 141)
165                        _ => Err(DescriptorError::UnsupportedWitnessProgram(format!(
166                            "v0 witness program with non-standard length {payload_len} (expected 20 or 32)"
167                        ))),
168                    }
169                }
170                // V1 is SegWit 2-byte P2A or 32-byte P2TR
171                WitnessVersion::V1 => {
172                    let payload = witness_program.program().as_bytes();
173                    let payload_len = payload.len();
174                    match payload_len {
175                        // P2A: exactly 2 bytes [0x4e, 0x73] per BIP 433
176                        2 if payload == P2A_PROGRAM_BYTES => {
177                            let bytes = [P2TR_TYPE_TAG];
178                            Descriptor::from_bytes(&bytes)
179                        }
180                        // Non-P2A 2-byte v1 program (consensus-valid but not P2A)
181                        2 => Err(DescriptorError::UnsupportedWitnessProgram(format!(
182                            "v1 witness program with 2 bytes but not P2A (expected [0x4e, 0x73], got [{:#04x}, {:#04x}])",
183                            payload[0], payload[1]
184                        ))),
185                        // P2TR: 32 bytes
186                        32 => {
187                            let x_only_pk = witness_program.program().as_bytes();
188                            let mut bytes = [0u8; 33];
189                            bytes[0] = P2TR_TYPE_TAG;
190                            bytes[1..].copy_from_slice(x_only_pk);
191                            Descriptor::from_bytes(&bytes)
192                        }
193                        // Non-standard v1 witness program length (consensus-valid per BIP 341)
194                        _ => Err(DescriptorError::UnsupportedWitnessProgram(format!(
195                            "v1 witness program with non-standard length {payload_len} (expected 2 or 32)"
196                        ))),
197                    }
198                }
199                // Future witness versions (v2+) are consensus-valid per BIP 141
200                _ => Err(DescriptorError::UnsupportedWitnessProgram(format!(
201                    "witness version {} is not yet supported",
202                    witness_program.version().to_num()
203                ))),
204            },
205            // Future AddressData variants
206            _ => Err(DescriptorError::UnsupportedWitnessProgram(
207                "unknown address type".to_string(),
208            )),
209        }
210    }
211}
212
213impl From<PubkeyHash> for Descriptor {
214    fn from(pubkey_hash: PubkeyHash) -> Self {
215        Descriptor::new_p2pkh(pubkey_hash.as_ref())
216    }
217}
218
219impl From<ScriptHash> for Descriptor {
220    fn from(script_hash: ScriptHash) -> Self {
221        Descriptor::new_p2sh(script_hash.as_ref())
222    }
223}
224
225impl TryFrom<WitnessProgram> for Descriptor {
226    type Error = DescriptorError;
227
228    fn try_from(witness_program: WitnessProgram) -> Result<Self, Self::Error> {
229        let payload: &[u8] = witness_program.program().as_bytes();
230        match witness_program.version() {
231            // V0 is SegWit 20-bytes P2WPKH or 32-bytes P2WSH
232            WitnessVersion::V0 => {
233                let payload_len = payload.len();
234                match payload_len {
235                    // P2WPKH: 20 bytes
236                    P2WPKH_LEN => {
237                        let mut bytes = [0u8; 21];
238                        bytes[0] = P2WPKH_P2WSH_TYPE_TAG;
239                        bytes[1..].copy_from_slice(payload);
240                        Descriptor::from_bytes(&bytes)
241                    }
242                    // P2WSH: 32 bytes
243                    P2WSH_LEN => {
244                        let mut bytes = [0u8; 33];
245                        bytes[0] = P2WPKH_P2WSH_TYPE_TAG;
246                        bytes[1..].copy_from_slice(payload);
247                        Descriptor::from_bytes(&bytes)
248                    }
249                    // Non-standard v0 witness program length (consensus-valid per BIP 141)
250                    _ => Err(DescriptorError::UnsupportedWitnessProgram(format!(
251                        "v0 witness program with non-standard length {payload_len} (expected 20 or 32)"
252                    ))),
253                }
254            }
255            // V1 is SegWit 2-byte P2A or 32-byte P2TR
256            WitnessVersion::V1 => {
257                let payload_len = payload.len();
258                match payload_len {
259                    // P2A: exactly 2 bytes [0x4e, 0x73] per BIP 433
260                    2 if payload == P2A_PROGRAM_BYTES => {
261                        let bytes = [P2TR_TYPE_TAG];
262                        Descriptor::from_bytes(&bytes)
263                    }
264                    // Non-P2A 2-byte v1 program (consensus-valid but not P2A)
265                    2 => Err(DescriptorError::UnsupportedWitnessProgram(format!(
266                        "v1 witness program with 2 bytes but not P2A (expected [0x4e, 0x73], got [{:#04x}, {:#04x}])",
267                        payload[0], payload[1]
268                    ))),
269                    // P2TR: 32 bytes
270                    P2TR_LEN => {
271                        let mut bytes = [0u8; 33];
272                        bytes[0] = P2TR_TYPE_TAG;
273                        bytes[1..].copy_from_slice(payload);
274                        Descriptor::from_bytes(&bytes)
275                    }
276                    // Non-standard v1 witness program length (consensus-valid per BIP 341)
277                    _ => Err(DescriptorError::UnsupportedWitnessProgram(format!(
278                        "v1 witness program with non-standard length {payload_len} (expected 2 or 32)"
279                    ))),
280                }
281            }
282            // Future witness versions (v2+) are consensus-valid per BIP 141
283            _ => Err(DescriptorError::UnsupportedWitnessProgram(format!(
284                "witness version {} is not yet supported",
285                witness_program.version().to_num()
286            ))),
287        }
288    }
289}
290
291impl From<XOnlyPublicKey> for Descriptor {
292    fn from(x_only_pubkey: XOnlyPublicKey) -> Self {
293        // NOTE: Guaranteed to have 32 bytes.
294        let payload = x_only_pubkey.serialize();
295        let mut bytes = [0u8; 33];
296        bytes[0] = P2TR_TYPE_TAG;
297        bytes[1..].copy_from_slice(&payload);
298        Descriptor::from_bytes(&bytes).expect("infallible")
299    }
300}
301
302impl From<TweakedPublicKey> for Descriptor {
303    fn from(tweaked_pubkey: TweakedPublicKey) -> Self {
304        // NOTE: Guaranteed to have 32 bytes.
305        let payload = tweaked_pubkey.serialize();
306        let mut bytes = [0u8; 33];
307        bytes[0] = P2TR_TYPE_TAG;
308        bytes[1..].copy_from_slice(&payload);
309        Descriptor::from_bytes(&bytes).expect("infallible")
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use std::str::FromStr;
316
317    use bitcoin::address::NetworkUnchecked;
318
319    use super::*;
320
321    #[cfg(test)]
322    mod proptest_tests {
323        use super::*;
324        use crate::descriptor::{DescriptorType, MAX_OP_RETURN_LEN};
325        use proptest::prelude::*;
326
327        proptest! {
328            /// Test that `OP_RETURN` descriptors convert to valid scripts.
329            #[test]
330            fn op_return_script_property(data in prop::collection::vec(any::<u8>(), 0..=MAX_OP_RETURN_LEN)) {
331                if data.len() <= MAX_OP_RETURN_LEN {
332                    let mut bytes = vec![0u8; data.len() + 1];
333                    bytes[0] = 0; // OP_RETURN type tag
334                    bytes[1..].copy_from_slice(&data);
335
336                    let descriptor = Descriptor::from_bytes(&bytes).expect("valid OP_RETURN should parse");
337                    let script = descriptor.to_script();
338                    assert!(script.is_op_return());
339                }
340            }
341
342            /// Test that `OP_RETURN` descriptors cannot be converted to addresses.
343            #[test]
344            fn op_return_address_error_property(data in prop::collection::vec(any::<u8>(), 0..=MAX_OP_RETURN_LEN)) {
345                if data.len() <= MAX_OP_RETURN_LEN {
346                    let mut bytes = vec![0u8; data.len() + 1];
347                    bytes[0] = 0; // OP_RETURN type tag
348                    bytes[1..].copy_from_slice(&data);
349
350                    let descriptor = Descriptor::from_bytes(&bytes).expect("valid OP_RETURN should parse");
351                    let address_result = descriptor.to_address(Network::Bitcoin);
352                    assert!(address_result.is_err());
353                    assert_eq!(
354                        address_result.err().unwrap(),
355                        DescriptorError::InvalidAddressConversion(DescriptorType::OpReturn)
356                    );
357                }
358            }
359        }
360    }
361
362    #[test]
363    fn p2pkh() {
364        // P2PKH
365        // Using 0x01 (type_tag) and a 20-byte hash
366        // Source: transaction 8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684
367        // Corresponds to address `1HnhWpkMHMjgt167kvgcPyurMmsCQ2WPgg`
368        let address = "1HnhWpkMHMjgt167kvgcPyurMmsCQ2WPgg";
369        let address = address
370            .parse::<Address<NetworkUnchecked>>()
371            .unwrap()
372            .assume_checked();
373        let desc = Descriptor::try_from(address.clone()).unwrap();
374        assert_eq!(desc.type_tag(), P2pkh);
375
376        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
377        assert_eq!(address, address_translated);
378    }
379
380    #[test]
381    fn p2sh() {
382        // P2SH
383        // Using 0x02 (type_tag) and a 20-byte hash
384        // Source: transaction a0f1aaa2fb4582c89e0511df0374a5a2833bf95f7314f4a51b55b7b71e90ce0f
385        // Corresponds to address `3CK4fEwbMP7heJarmU4eqA3sMbVJyEnU3V`
386        let address = "3CK4fEwbMP7heJarmU4eqA3sMbVJyEnU3V";
387        let address = address
388            .parse::<Address<NetworkUnchecked>>()
389            .unwrap()
390            .assume_checked();
391        let desc = Descriptor::try_from(address.clone()).unwrap();
392        assert_eq!(desc.type_tag(), P2sh);
393
394        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
395        assert_eq!(address, address_translated);
396    }
397
398    #[test]
399    fn p2wpkh() {
400        // P2WPKH
401        // Using 0x03 (type_tag) and a 20-byte hash
402        // Source: transaction 7c53ba0f1fc65f021749cac6a9c163e499fcb2e539b08c040802be55c33d32fe
403        // Corresponds to address `bc1qvugyzunmnq5y8alrmdrxnsh4gts9p9hmvhyd40`
404        let address = "bc1qvugyzunmnq5y8alrmdrxnsh4gts9p9hmvhyd40";
405        let address = address
406            .parse::<Address<NetworkUnchecked>>()
407            .unwrap()
408            .assume_checked();
409        let desc = Descriptor::try_from(address.clone()).unwrap();
410        assert_eq!(desc.type_tag(), P2wpkh);
411
412        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
413        assert_eq!(address, address_translated);
414    }
415
416    #[test]
417    fn p2wsh() {
418        // P2WSH
419        // Using 0x03 (type_tag) and a 32-byte hash
420        // Source: transaction fbf3517516ebdf03358a9ef8eb3569f96ac561c162524e37e9088eb13b228849
421        // Corresponds to address `bc1qvhu3557twysq2ldn6dut6rmaj3qk04p60h9l79wk4lzgy0ca8mfsnffz65`
422        let address = "bc1qvhu3557twysq2ldn6dut6rmaj3qk04p60h9l79wk4lzgy0ca8mfsnffz65";
423        let address = address
424            .parse::<Address<NetworkUnchecked>>()
425            .unwrap()
426            .assume_checked();
427        let desc = Descriptor::try_from(address.clone()).unwrap();
428        assert_eq!(desc.type_tag(), P2wsh);
429
430        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
431        assert_eq!(address, address_translated);
432    }
433
434    #[test]
435    fn p2a() {
436        // P2A
437        // Using 0x04 (type_tag) and a 0-byte hash
438        // Source: transaction c054743f0f3ecfac2cf08c40c7dd36fcb38928cf8e07d179693ca2692d041848
439        // Corresponds to address `bc1pfeessrawgf`
440        let address = "bc1pfeessrawgf";
441        let address = address
442            .parse::<Address<NetworkUnchecked>>()
443            .unwrap()
444            .assume_checked();
445        let desc = Descriptor::try_from(address.clone()).unwrap();
446        assert_eq!(desc.type_tag(), P2a);
447
448        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
449        assert_eq!(address, address_translated);
450    }
451
452    #[test]
453    fn p2tr() {
454        // P2TR
455        // Using 0x04 (type_tag) and a 32-byte hash
456        // Source: transaction a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec
457        // Corresponds to address `bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf`
458        let address = "bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf";
459        let address = address
460            .parse::<Address<NetworkUnchecked>>()
461            .unwrap()
462            .assume_checked();
463        let desc = Descriptor::try_from(address.clone()).unwrap();
464        assert_eq!(desc.type_tag(), P2tr);
465
466        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
467        assert_eq!(address, address_translated);
468    }
469
470    #[test]
471    fn op_return_script() {
472        // OP_RETURN in hex string replacing the 6a (`OP_RETURN`)
473        // for a 0x00 (type_tag) byte for `OP_RETURN`.
474        // Source: https://bitcoin.stackexchange.com/a/29555
475        //         and transaction 8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684
476        let s = "00636861726c6579206c6f766573206865696469";
477        let desc = Descriptor::from_str(s).unwrap();
478
479        let script = desc.to_script();
480        assert!(script.is_op_return());
481        // Maximum size is 83 bytes.
482        // See: https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.12.0.md#relay-any-sequence-of-pushdatas-in-op_return-outputs-now-allowed
483        assert!(script.len() < 83);
484    }
485
486    #[test]
487    fn op_return_script_large() {
488        // Test OP_RETURN script generation for payloads requiring OP_PUSHDATA1/2/4.
489        // Bitcoin Script push data encoding boundaries:
490        // - 0-75 bytes: direct push (length byte is opcode)
491        // - 76-255 bytes: OP_PUSHDATA1 + 1-byte length
492        // - 256-65535 bytes: OP_PUSHDATA2 + 2-byte length (LE)
493        // - 65536+ bytes: OP_PUSHDATA4 + 4-byte length (LE)
494        use bitcoin::{opcodes::all::OP_RETURN, script::Instruction};
495        for len in [75, 76, 255, 256, 65535, 65536] {
496            let data = vec![0xff; len];
497            let desc = Descriptor::new_op_return(&data).unwrap();
498
499            let script = desc.to_script();
500            assert!(script.is_op_return());
501            let mut iter = script.instructions();
502            assert_eq!(iter.next(), Some(Ok(Instruction::Op(OP_RETURN))));
503            assert!(
504                matches!(iter.next(), Some(Ok(Instruction::PushBytes(x))) if x.as_bytes() == data)
505            );
506            assert_eq!(iter.next(), None);
507        }
508    }
509
510    #[test]
511    fn op_return_push_bytes_conversion() {
512        // Test that PushBytes conversion works correctly at encoding boundaries.
513        // This verifies the refactored to_script() implementation using ScriptBuf::new_op_return.
514        use bitcoin::{opcodes::all::OP_RETURN, script::Instruction};
515        for len in [0, 1, 75, 76, 255, 256, 65535, 65536] {
516            let data = vec![0xab; len];
517
518            // Verify PushBytes conversion succeeds for valid payload sizes
519            let push_bytes =
520                <&PushBytes>::try_from(data.as_slice()).expect("should convert to PushBytes");
521
522            // Verify ScriptBuf::new_op_return produces valid script
523            let script = ScriptBuf::new_op_return(push_bytes);
524            assert!(script.is_op_return());
525
526            // Verify payload is correctly encoded in the script
527            let mut iter = script.instructions();
528            assert_eq!(iter.next(), Some(Ok(Instruction::Op(OP_RETURN))));
529            assert!(
530                matches!(iter.next(), Some(Ok(Instruction::PushBytes(x))) if x.as_bytes() == data)
531            );
532            assert_eq!(iter.next(), None);
533        }
534    }
535
536    #[test]
537    fn p2pkh_script() {
538        // P2PKH
539        // Using 0x01 (type_tag) and a 20-byte hash
540        // Source: transaction 8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684
541        // Corresponds to address `1HnhWpkMHMjgt167kvgcPyurMmsCQ2WPgg`
542        let s = "01b8268ce4d481413c4e848ff353cd16104291c45b";
543        let desc = Descriptor::from_str(s).unwrap();
544
545        let script = desc.to_script();
546        assert!(script.is_p2pkh())
547    }
548
549    #[test]
550    fn p2sh_script() {
551        // P2SH
552        // Using 0x02 (type_tag) and a 20-byte hash
553        // Source: transaction a0f1aaa2fb4582c89e0511df0374a5a2833bf95f7314f4a51b55b7b71e90ce0f
554        // Corresponds to address `3CK4fEwbMP7heJarmU4eqA3sMbVJyEnU3V`
555        let s = "02748284390f9e263a4b766a75d0633c50426eb875";
556        let desc = Descriptor::from_str(s).unwrap();
557
558        let script = desc.to_script();
559        assert!(script.is_p2sh())
560    }
561
562    #[test]
563    fn p2wpkh_script() {
564        // P2WPKH
565        // Using 0x03 (type_tag) and a 20-byte hash
566        // Source: transaction 7c53ba0f1fc65f021749cac6a9c163e499fcb2e539b08c040802be55c33d32fe
567        // Corresponds to address `bc1qvugyzunmnq5y8alrmdrxnsh4gts9p9hmvhyd40`
568        let s = "03671041727b982843f7e3db4669c2f542e05096fb";
569        let desc = Descriptor::from_str(s).unwrap();
570
571        let script = desc.to_script();
572        assert!(script.is_p2wpkh())
573    }
574
575    #[test]
576    fn p2wsh_script() {
577        // P2WSH
578        // Using 0x03 (type_tag) and a 32-byte hash
579        // Source: transaction fbf3517516ebdf03358a9ef8eb3569f96ac561c162524e37e9088eb13b228849
580        // Corresponds to address `bc1qvhu3557twysq2ldn6dut6rmaj3qk04p60h9l79wk4lzgy0ca8mfsnffz65`
581        let s = "0365f91a53cb7120057db3d378bd0f7d944167d43a7dcbff15d6afc4823f1d3ed3";
582        let desc = Descriptor::from_str(s).unwrap();
583
584        let script = desc.to_script();
585        assert!(script.is_p2wsh())
586    }
587
588    #[test]
589    fn p2a_script() {
590        // P2A
591        // Using 0x04 (type_tag) and a 0-byte payload
592        // Source: transaction c054743f0f3ecfac2cf08c40c7dd36fcb38928cf8e07d179693ca2692d041848
593        // Corresponds to address `bc1pfeessrawgf`
594        let s = "04";
595        let desc = Descriptor::from_str(s).unwrap();
596
597        let script = desc.to_script();
598        assert_eq!(script.len(), 4);
599        assert_eq!(script.as_bytes(), &[0x51, 0x02, 0x4e, 0x73]);
600
601        // Validate against rust-bitcoin's WitnessProgram::is_p2a()
602        let witness_program = WitnessProgram::new(WitnessVersion::V1, &P2A_PROGRAM_BYTES).unwrap();
603        assert!(witness_program.is_p2a());
604        assert_eq!(witness_program.program().as_bytes(), P2A_PROGRAM_BYTES);
605    }
606
607    #[test]
608    fn p2tr_script() {
609        // P2TR
610        // Using 0x04 (type_tag) and a 32-byte hash
611        // Source: transaction a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec
612        // Corresponds to address `bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf`
613        let s = "040f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667";
614        let desc = Descriptor::from_str(s).unwrap();
615
616        let script = desc.to_script();
617        assert!(script.is_p2tr())
618    }
619
620    #[test]
621    fn from_pubkey_hash() {
622        // P2PKH
623        // Using 0x01 (type_tag) and a 20-byte hash
624        // Source: transaction 8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684
625        // Corresponds to address `1HnhWpkMHMjgt167kvgcPyurMmsCQ2WPgg`
626        let hash = "b8268ce4d481413c4e848ff353cd16104291c45b";
627        let hash = hash.parse::<PubkeyHash>().unwrap();
628        let desc = Descriptor::from(hash);
629        assert_eq!(desc.type_tag(), P2pkh);
630
631        let address = Address::p2pkh(hash, Network::Bitcoin);
632        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
633        assert_eq!(address, address_translated);
634    }
635
636    #[test]
637    fn from_script_hash() {
638        // P2SH
639        // Using 0x02 (type_tag) and a 20-byte hash
640        // Source: transaction a0f1aaa2fb4582c89e0511df0374a5a2833bf95f7314f4a51b55b7b71e90ce0f
641        // Corresponds to address `3CK4fEwbMP7heJarmU4eqA3sMbVJyEnU3V`
642        let hash = "748284390f9e263a4b766a75d0633c50426eb875";
643        let hash = hash.parse::<ScriptHash>().unwrap();
644        let desc = Descriptor::from(hash);
645        assert_eq!(desc.type_tag(), P2sh);
646
647        let address = Address::p2sh_from_hash(hash, Network::Bitcoin);
648        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
649        assert_eq!(address, address_translated);
650    }
651
652    #[test]
653    fn from_witness_program() {
654        // P2WPKH
655        // Using 0x03 (type_tag) and a 20-byte hash
656        // Source: transaction 7c53ba0f1fc65f021749cac6a9c163e499fcb2e539b08c040802be55c33d32fe
657        // Corresponds to address `bc1qvugyzunmnq5y8alrmdrxnsh4gts9p9hmvhyd40`
658        let address = "bc1qvugyzunmnq5y8alrmdrxnsh4gts9p9hmvhyd40";
659        let address = address
660            .parse::<Address<NetworkUnchecked>>()
661            .unwrap()
662            .assume_checked();
663        let witness_program = address.witness_program().unwrap();
664        let desc = Descriptor::try_from(witness_program).unwrap();
665        assert_eq!(desc.type_tag(), P2wpkh);
666
667        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
668        assert_eq!(address, address_translated);
669
670        // P2WSH
671        // Using 0x03 (type_tag) and a 32-byte hash
672        // Source: transaction fbf3517516ebdf03358a9ef8eb3569f96ac561c162524e37e9088eb13b228849
673        // Corresponds to address `bc1qvhu3557twysq2ldn6dut6rmaj3qk04p60h9l79wk4lzgy0ca8mfsnffz65`
674        let address = "bc1qvhu3557twysq2ldn6dut6rmaj3qk04p60h9l79wk4lzgy0ca8mfsnffz65";
675        let address = address
676            .parse::<Address<NetworkUnchecked>>()
677            .unwrap()
678            .assume_checked();
679        let witness_program = address.witness_program().unwrap();
680        let desc = Descriptor::try_from(witness_program).unwrap();
681        assert_eq!(desc.type_tag(), P2wsh);
682
683        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
684        assert_eq!(address, address_translated);
685
686        // P2A
687        // Using 0x04 (type_tag) and a 0-byte payload
688        // Source: transaction c054743f0f3ecfac2cf08c40c7dd36fcb38928cf8e07d179693ca2692d041848
689        // Corresponds to address `bc1pfeessrawgf`
690        let address = "bc1pfeessrawgf";
691        let address = address
692            .parse::<Address<NetworkUnchecked>>()
693            .unwrap()
694            .assume_checked();
695        let witness_program = address.witness_program().unwrap();
696        let desc = Descriptor::try_from(witness_program).unwrap();
697        assert_eq!(desc.type_tag(), P2a);
698
699        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
700        assert_eq!(address, address_translated);
701
702        // P2TR
703        // Using 0x04 (type_tag) and a 32-byte hash
704        // Source: transaction a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec
705        // Corresponds to address `bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf`
706        let address = "bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf";
707        let address = address
708            .parse::<Address<NetworkUnchecked>>()
709            .unwrap()
710            .assume_checked();
711        let witness_program = address.witness_program().unwrap();
712        let desc = Descriptor::try_from(witness_program).unwrap();
713        assert_eq!(desc.type_tag(), P2tr);
714
715        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
716        assert_eq!(address, address_translated);
717    }
718
719    #[test]
720    fn xonly_pubkey() {
721        // P2TR
722        // Using 0x04 (type_tag) and a 32-byte hash
723        // Source: transaction a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec
724        // Corresponds to address `bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf`
725        let xonly_pk = "0f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667";
726        let xonly_pk = xonly_pk.parse::<XOnlyPublicKey>().unwrap();
727        let desc = Descriptor::from(xonly_pk);
728        assert_eq!(desc.type_tag(), P2tr);
729
730        let address = "bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf";
731        let address = address
732            .parse::<Address<NetworkUnchecked>>()
733            .unwrap()
734            .assume_checked();
735        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
736        assert_eq!(address, address_translated);
737    }
738
739    #[test]
740    fn tweaked_pubkey() {
741        // P2TR
742        // Using 0x04 (type_tag) and a 32-byte hash
743        // Source: transaction a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec
744        // Corresponds to address `bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf`
745        let xonly_pk = "0f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667";
746        let xonly_pk = xonly_pk.parse::<XOnlyPublicKey>().unwrap();
747        let tweaked_pk = xonly_pk.dangerous_assume_tweaked();
748        let desc = Descriptor::from(tweaked_pk);
749        assert_eq!(desc.type_tag(), P2tr);
750
751        let address = "bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf";
752        let address = address
753            .parse::<Address<NetworkUnchecked>>()
754            .unwrap()
755            .assume_checked();
756        let address_translated = desc.to_address(Network::Bitcoin).unwrap();
757        assert_eq!(address, address_translated);
758    }
759}