bitcoin_bosd/
descriptor.rs

1//! # Bitcoin Output Script Descriptor (BOSD)
2//!
3//! This module implements a BOSD parser and validator.
4//!
5//! The main type is [`Descriptor`].
6//! Check this crate's top-level documentation for the
7//! specification and rationale.
8
9use core::fmt;
10
11use std::{
12    fmt::{Display, Formatter},
13    str::FromStr,
14};
15
16use hex::{DisplayHex, FromHex};
17
18#[cfg(feature = "address")]
19use bitcoin::XOnlyPublicKey;
20
21use crate::error::DescriptorError;
22
23/// `OP_RETURN` type tag.
24pub(crate) const OP_RETURN_TYPE_TAG: u8 = 0;
25
26/// Maximum length of `OP_RETURN` payload.
27pub const MAX_OP_RETURN_LEN: usize = 100_000;
28
29/// `P2PKH` type tag.
30pub(crate) const P2PKH_TYPE_TAG: u8 = 1;
31
32/// Exact length of P2PKH payload.
33pub const P2PKH_LEN: usize = 20;
34
35/// `P2SH` type tag.
36pub(crate) const P2SH_TYPE_TAG: u8 = 2;
37
38/// Exact length of P2SH payload.
39pub const P2SH_LEN: usize = 20;
40
41/// `P2WPKH`/`P2WSH` type tag.
42pub(crate) const P2WPKH_P2WSH_TYPE_TAG: u8 = 3;
43
44/// Exact length of P2WPKH payload.
45pub const P2WPKH_LEN: usize = 20;
46
47/// Exact length of P2WSH payload.
48pub const P2WSH_LEN: usize = 32;
49
50/// `P2A`/`P2TR` type tag.
51pub(crate) const P2TR_TYPE_TAG: u8 = 4;
52
53/// Exact length of `P2A` payload.
54pub const P2A_LEN: usize = 0;
55
56/// Exact length of P2TR payload.
57pub const P2TR_LEN: usize = 32;
58
59/// A Bitcoin Output Script Descriptor (BOSD).
60///
61/// This is a compact binary format consisting of
62/// a `type_tag` that represents a ScriptPubKey that can be
63/// relayed by any node in the Bitcoin network,
64/// due to standardness requirements.
65///
66/// See [the Bitcoin developer guide on Transactions](https://developer.bitcoin.org/devguide/transactions.html)
67/// for more information on standardness.
68#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69#[repr(C)]
70pub struct Descriptor {
71    /// The type of the descriptor.
72    type_tag: DescriptorType,
73
74    /// The actual underlying data.
75    payload: Vec<u8>,
76}
77
78impl Descriptor {
79    /// Constructs a new [`Descriptor`] from a byte slice.
80    ///
81    /// Users are advised to use the `new_*` methods whenever possible.
82    pub fn from_bytes(bytes: &[u8]) -> Result<Self, DescriptorError> {
83        // Extract the type tag (which must exist) and the payload
84        let (&type_tag, payload) = bytes.split_first().ok_or(DescriptorError::MissingTypeTag)?;
85
86        // Validate the payload length against the type
87        match type_tag {
88            // OP_RETURN must be at most 100KB.
89            OP_RETURN_TYPE_TAG => {
90                let payload_len = payload.len();
91                if payload_len > MAX_OP_RETURN_LEN {
92                    Err(DescriptorError::InvalidPayloadLength(payload_len))
93                } else {
94                    Ok(Self {
95                        type_tag: DescriptorType::OpReturn,
96                        payload: payload.to_vec(),
97                    })
98                }
99            }
100            // P2PKH and P2SH must be exactly 20 bytes.
101            P2PKH_TYPE_TAG => {
102                let payload_len = payload.len();
103                if payload_len != P2PKH_LEN {
104                    Err(DescriptorError::InvalidPayloadLength(payload_len))
105                } else {
106                    Ok(Self {
107                        type_tag: DescriptorType::P2pkh,
108                        payload: payload.to_vec(),
109                    })
110                }
111            }
112            P2SH_TYPE_TAG => {
113                let payload_len = payload.len();
114                if payload_len != P2SH_LEN {
115                    Err(DescriptorError::InvalidPayloadLength(payload_len))
116                } else {
117                    Ok(Self {
118                        type_tag: DescriptorType::P2sh,
119                        payload: payload.to_vec(),
120                    })
121                }
122            }
123            // P2WPKH must be exactly 20 bytes, and P2SH must be exactly 32 bytes.
124            P2WPKH_P2WSH_TYPE_TAG => {
125                let payload_len = payload.len();
126                match payload_len {
127                    P2WPKH_LEN => Ok(Self {
128                        type_tag: DescriptorType::P2wpkh,
129                        payload: payload.to_vec(),
130                    }),
131                    P2WSH_LEN => Ok(Self {
132                        type_tag: DescriptorType::P2wsh,
133                        payload: payload.to_vec(),
134                    }),
135                    _ => Err(DescriptorError::InvalidPayloadLength(payload_len)),
136                }
137            }
138            // P2A must be exactly 0 bytes, and P2TR must be exactly 32 bytes.
139            P2TR_TYPE_TAG => {
140                let payload_len = payload.len();
141                match payload_len {
142                    P2A_LEN => Ok(Self {
143                        type_tag: DescriptorType::P2a,
144                        payload: payload.to_vec(),
145                    }),
146                    P2TR_LEN => Ok(Self {
147                        type_tag: DescriptorType::P2tr,
148                        payload: payload.to_vec(),
149                    }),
150                    _ => Err(DescriptorError::InvalidPayloadLength(payload_len)),
151                }
152            }
153            _ => Err(DescriptorError::InvalidDescriptorType(type_tag)),
154        }
155    }
156
157    /// Constructs a new [`Descriptor`] from a byte [`Vec`].
158    ///
159    /// Users are advised to use the `new_*` methods whenever possible.
160    pub fn from_vec(bytes: Vec<u8>) -> Result<Self, DescriptorError> {
161        Self::from_bytes(&bytes)
162    }
163
164    /// Constructs a new [`Descriptor`] from an `OP_RETURN` payload.
165    ///
166    /// The payload is expected to be at most 100KB.
167    ///
168    /// # Example
169    ///
170    /// ```
171    /// # use bitcoin_bosd::{Descriptor, DescriptorType};
172    /// let payload = b"hello world";
173    /// let desc = Descriptor::new_op_return(payload).expect("valid payload that is at most 100KB");
174    /// # assert_eq!(desc.type_tag(), DescriptorType::OpReturn);
175    /// # assert_eq!(desc.payload(), b"hello world");
176    /// ```
177    pub fn new_op_return(payload: &[u8]) -> Result<Self, DescriptorError> {
178        let type_tag = DescriptorType::OpReturn;
179        let payload_len = payload.len();
180        if payload_len > MAX_OP_RETURN_LEN {
181            Err(DescriptorError::InvalidPayloadLength(payload_len))
182        } else {
183            Ok(Self {
184                type_tag,
185                payload: payload.to_vec(),
186            })
187        }
188    }
189
190    /// Constructs a new [`Descriptor`] from a P2PKH payload.
191    ///
192    /// The payload is expected to be a valid 20-byte hash.
193    ///
194    /// # Example
195    ///
196    /// ```
197    /// # use bitcoin_bosd::{Descriptor, DescriptorType, descriptor::P2PKH_LEN};
198    /// let payload = [0u8; P2PKH_LEN]; // all zeros, don't use in production
199    /// let desc = Descriptor::new_p2pkh(&payload);
200    /// # assert_eq!(desc.type_tag(), DescriptorType::P2pkh);
201    /// # assert_eq!(desc.payload(), [0u8; P2PKH_LEN]);
202    /// ```
203    pub fn new_p2pkh(payload: &[u8; P2PKH_LEN]) -> Self {
204        let type_tag = DescriptorType::P2pkh;
205        Self {
206            type_tag,
207            payload: payload.to_vec(),
208        }
209    }
210
211    /// Constructs a new [`Descriptor`] from a P2SH payload.
212    ///
213    /// The payload is expected to be a valid 20-byte hash.
214    ///
215    /// # Example
216    ///
217    /// ```
218    /// # use bitcoin_bosd::{Descriptor, DescriptorType, descriptor::P2SH_LEN};
219    /// let payload = [0u8; P2SH_LEN]; // all zeros, don't use in production
220    /// let desc = Descriptor::new_p2sh(&payload);
221    /// # assert_eq!(desc.type_tag(), DescriptorType::P2sh);
222    /// # assert_eq!(desc.payload(), [0u8; P2SH_LEN]);
223    /// ```
224    pub fn new_p2sh(payload: &[u8; P2SH_LEN]) -> Self {
225        let type_tag = DescriptorType::P2sh;
226        Self {
227            type_tag,
228            payload: payload.to_vec(),
229        }
230    }
231
232    /// Constructs a new [`Descriptor`] from a P2WPKH payload.
233    ///
234    /// The payload is expected to be a valid 20-byte hash.
235    ///
236    /// # Example
237    ///
238    /// ```
239    /// # use bitcoin_bosd::{Descriptor, DescriptorType, descriptor::P2WPKH_LEN};
240    /// let payload = [0u8; P2WPKH_LEN]; // all zeros, don't use in production
241    /// let desc = Descriptor::new_p2wpkh(&payload);
242    /// # assert_eq!(desc.type_tag(), DescriptorType::P2wpkh);
243    /// # assert_eq!(desc.payload(), [0u8; P2WPKH_LEN]);
244    /// ```
245    pub fn new_p2wpkh(payload: &[u8; P2WPKH_LEN]) -> Self {
246        let type_tag = DescriptorType::P2wpkh;
247        Self {
248            type_tag,
249            payload: payload.to_vec(),
250        }
251    }
252
253    /// Constructs a new [`Descriptor`] from a P2WSH payload.
254    ///
255    /// The payload is expected to be a valid 32-byte hash.
256    ///
257    /// # Example
258    ///
259    /// ```
260    /// # use bitcoin_bosd::{Descriptor, DescriptorType, descriptor::P2WSH_LEN};
261    /// let payload = [0u8; P2WSH_LEN]; // all zeros, don't use in production
262    /// let desc = Descriptor::new_p2wsh(&payload);
263    /// # assert_eq!(desc.type_tag(), DescriptorType::P2wsh);
264    /// # assert_eq!(desc.payload(), [0u8; P2WSH_LEN]);
265    /// ```
266    pub fn new_p2wsh(payload: &[u8; P2WSH_LEN]) -> Self {
267        let type_tag = DescriptorType::P2wsh;
268        Self {
269            type_tag,
270            payload: payload.to_vec(),
271        }
272    }
273
274    /// Constructs a new [`Descriptor`] from an empty P2A payload.
275    ///
276    /// # Example
277    ///
278    /// ```
279    /// # use bitcoin_bosd::{Descriptor, DescriptorType, descriptor::P2A_LEN};
280    /// let payload = [0u8; P2A_LEN]; // empty payload
281    /// let desc = Descriptor::new_p2a(&payload);
282    /// # assert_eq!(desc.type_tag(), DescriptorType::P2a);
283    /// # assert_eq!(desc.payload(), [0u8; P2A_LEN]);
284    /// ```
285    pub fn new_p2a(payload: &[u8; P2A_LEN]) -> Self {
286        let type_tag = DescriptorType::P2a;
287
288        Self {
289            type_tag,
290            payload: payload.to_vec(),
291        }
292    }
293
294    /// Constructs a new [`Descriptor`] from an _unchecked_ P2TR payload.
295    ///
296    /// The payload is expected to be a valid 32-byte X-only public key.
297    /// You _must_ validate this key on your own; this function will not do it for you.
298    ///
299    /// # Example
300    ///
301    /// ```
302    /// # use bitcoin_bosd::{Descriptor, DescriptorType, descriptor::P2TR_LEN};
303    /// let payload = [2u8; P2TR_LEN]; // valid X-only public key, but don't use in production
304    /// let desc = Descriptor::new_p2tr_unchecked(&payload);
305    /// # assert_eq!(desc.type_tag(), DescriptorType::P2tr);
306    /// # assert_eq!(desc.payload(), [2u8; P2TR_LEN]);
307    /// ```
308    pub fn new_p2tr_unchecked(payload: &[u8; P2TR_LEN]) -> Self {
309        let type_tag = DescriptorType::P2tr;
310
311        Self {
312            type_tag,
313            payload: payload.to_vec(),
314        }
315    }
316
317    /// Constructs a new [`Descriptor`] from a P2TR payload.
318    ///
319    /// The payload is expected to be a valid 32-byte X-only public key.
320    /// This function will validate this key for you, and return an error if validation fails.
321    ///
322    /// # Example
323    ///
324    /// ```
325    /// # use bitcoin_bosd::{Descriptor, DescriptorType, descriptor::P2TR_LEN};
326    /// let payload = [2u8; P2TR_LEN]; // valid X-only public key, but don't use in production
327    /// let desc = Descriptor::new_p2tr(&payload).expect("valid X-only public key");
328    /// # assert_eq!(desc.type_tag(), DescriptorType::P2tr);
329    /// # assert_eq!(desc.payload(), [2u8; P2TR_LEN]);
330    /// ```
331    #[cfg(feature = "address")]
332    pub fn new_p2tr(payload: &[u8; P2TR_LEN]) -> Result<Self, DescriptorError> {
333        let type_tag = DescriptorType::P2tr;
334
335        if XOnlyPublicKey::from_slice(payload).is_err() {
336            Err(DescriptorError::InvalidXOnlyPublicKey)
337        } else {
338            Ok(Self {
339                type_tag,
340                payload: payload.to_vec(),
341            })
342        }
343    }
344
345    /// Returns the bytes representation of the descriptor.
346    ///
347    /// That is:
348    ///
349    /// - 1-byte type tag.
350    /// - arbitrary-sized payload.
351    pub fn to_bytes(&self) -> Vec<u8> {
352        let mut bytes = Vec::with_capacity(1 + self.payload.len());
353        bytes.push(self.type_tag.to_u8());
354        bytes.extend_from_slice(&self.payload);
355        bytes
356    }
357
358    /// Generates fixed bytes of payload of length specified by the generic parameter.
359    ///
360    /// # Notes
361    ///
362    /// - This method is intended for internal use and relies on the caller
363    ///   ensuring that the payload's length matches the size `B`.
364    pub(crate) fn to_fixed_payload_bytes<const B: usize>(&self) -> [u8; B] {
365        debug_assert_eq!(self.payload().len(), B);
366        let mut bytes = [0u8; B];
367        bytes[..].copy_from_slice(self.payload());
368        bytes
369    }
370
371    /// Returns the type tag of the descriptor.
372    pub fn type_tag(&self) -> DescriptorType {
373        self.type_tag
374    }
375
376    /// Returns the payload of the descriptor.
377    ///
378    /// # Warning
379    ///
380    /// It is not advisable to use this method.
381    /// Instead, try to parse it either as a Bitcoin address
382    /// by using [`Descriptor::to_address`] in the case of an address,
383    /// or as a Bitcoin script by using [`Descriptor::to_script`] in
384    /// the case of an `OP_RETURN` payload.
385    pub fn payload(&self) -> &[u8] {
386        self.payload.as_slice()
387    }
388}
389
390impl Display for Descriptor {
391    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
392        let type_tag = self.type_tag().to_u8();
393        write!(f, "{}{}", &[type_tag].as_hex(), self.payload.as_hex())
394    }
395}
396
397impl FromStr for Descriptor {
398    type Err = DescriptorError;
399
400    fn from_str(s: &str) -> Result<Self, Self::Err> {
401        let bytes = Vec::from_hex(s)?;
402        Self::from_bytes(&bytes)
403    }
404}
405
406/// The type tag of a [`Descriptor`].
407///
408/// This is the first byte of the payload.
409#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
410#[non_exhaustive] // Might need more in the future.
411pub enum DescriptorType {
412    /// `OP_RETURN` payload.
413    OpReturn,
414
415    /// P2PKH hash.
416    ///
417    /// It is a 20-byte hash of a public key,
418    /// that is first hashed with SHA-256,
419    /// followed by RIPEMD-160.
420    P2pkh,
421
422    /// P2SH hash.
423    ///
424    /// It is a 20-byte hash of a custom locking script,
425    /// that is first hashed with SHA-256,
426    /// followed by RIPEMD-160.
427    P2sh,
428
429    /// P2WPKH hash.
430    ///
431    /// It is a 20-byte hash of a public key,
432    /// that is first hashed with SHA-256,
433    /// followed by RIPEMD-160.
434    P2wpkh,
435
436    /// P2WSH hash.
437    ///
438    /// It is a 32-byte hash of a custom locking script
439    /// hashed with SHA-256.
440    P2wsh,
441
442    ///  P2A.
443    ///
444    /// An anchor's descriptor has no payload.
445    P2a,
446
447    /// P2TR X-only public key.
448    ///
449    /// It is a 32-byte public key.
450    /// The key might be tweaked by a Merkle root hash
451    /// that represents the underlying taptree of script
452    /// spending conditions.
453    P2tr,
454}
455
456impl DescriptorType {
457    /// Returns the type tag as a byte.
458    pub fn to_u8(self) -> u8 {
459        match self {
460            DescriptorType::OpReturn => 0,
461            DescriptorType::P2pkh => 1,
462            DescriptorType::P2sh => 2,
463            DescriptorType::P2wpkh => 3,
464            DescriptorType::P2wsh => 3,
465            DescriptorType::P2a => 4,
466            DescriptorType::P2tr => 4,
467        }
468    }
469}
470
471impl Display for DescriptorType {
472    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473        match self {
474            DescriptorType::OpReturn => write!(f, "OP_RETURN"),
475            DescriptorType::P2pkh => write!(f, "P2PKH"),
476            DescriptorType::P2sh => write!(f, "P2SH"),
477            DescriptorType::P2wpkh => write!(f, "P2WPKH"),
478            DescriptorType::P2wsh => write!(f, "P2WSH"),
479            DescriptorType::P2a => write!(f, "P2A"),
480            DescriptorType::P2tr => write!(f, "P2TR"),
481        }
482    }
483}
484
485#[cfg(test)]
486mod tests {
487    use super::*;
488
489    #[cfg(test)]
490    mod proptest_tests {
491        use super::*;
492        use proptest::prelude::*;
493
494        proptest! {
495            /// Test that any valid `OP_RETURN` payload (0-100KB) roundtrips correctly.
496            #[test]
497            fn op_return_roundtrip_property(data in prop::collection::vec(any::<u8>(), 0..=MAX_OP_RETURN_LEN)) {
498                if data.len() <= MAX_OP_RETURN_LEN {
499                    let mut bytes = vec![0u8; data.len() + 1];
500                    bytes[0] = 0; // OP_RETURN type tag
501                    bytes[1..].copy_from_slice(&data);
502
503                    let descriptor = Descriptor::from_bytes(&bytes).expect("valid OP_RETURN should parse");
504                    assert_eq!(descriptor.type_tag(), DescriptorType::OpReturn);
505                    assert_eq!(descriptor.payload(), &data);
506                    assert_eq!(&descriptor.to_bytes(), &bytes);
507                }
508            }
509
510            /// Test that `OP_RETURN` payloads larger than 100KB are rejected.
511            #[test]
512            fn op_return_invalid_size_property(data in prop::collection::vec(any::<u8>(), (MAX_OP_RETURN_LEN + 1)..=(MAX_OP_RETURN_LEN * 2))) {
513                let mut bytes = vec![0u8; data.len() + 1];
514                bytes[0] = 0; // OP_RETURN type tag
515                bytes[1..].copy_from_slice(&data);
516
517                assert!(Descriptor::from_bytes(&bytes).is_err(),
518                    "OP_RETURN payload of {} bytes should be rejected", data.len());
519            }
520
521            /// Test that exactly 100KB `OP_RETURN` payloads are accepted.
522            #[test]
523            fn op_return_max_size_property(data in prop::collection::vec(any::<u8>(), MAX_OP_RETURN_LEN..=MAX_OP_RETURN_LEN)) {
524                let mut bytes = vec![0u8; data.len() + 1];
525                bytes[0] = 0; // OP_RETURN type tag
526                bytes[1..].copy_from_slice(&data);
527
528                let descriptor = Descriptor::from_bytes(&bytes).expect("100KB OP_RETURN should be valid");
529                assert_eq!(descriptor.type_tag(), DescriptorType::OpReturn);
530                assert_eq!(descriptor.payload(), &data);
531                assert_eq!(&descriptor.to_bytes(), &bytes);
532            }
533
534            /// Test that any valid descriptor roundtrips correctly.
535            #[test]
536            fn descriptor_roundtrip_property(data in prop::collection::vec(any::<u8>(), 1..=(MAX_OP_RETURN_LEN + 1))) {
537                if let Ok(descriptor) = Descriptor::from_bytes(&data) {
538                    assert_eq!(&descriptor.to_bytes(), &data);
539                }
540            }
541        }
542    }
543
544    #[test]
545    fn descriptor_from_bytes() {
546        let bytes = [0, 1, 2, 3, 4, 5];
547        let descriptor = Descriptor::from_bytes(&bytes).unwrap();
548        assert_eq!(descriptor.type_tag(), DescriptorType::OpReturn);
549        assert_eq!(descriptor.payload(), &[1, 2, 3, 4, 5]);
550    }
551
552    #[test]
553    fn descriptor_from_bytes_invalid() {
554        // Empty byte slice
555        let bytes = [];
556        assert!(Descriptor::from_bytes(&bytes).is_err());
557
558        // Only tag type byte
559        for type_tag in 0..=u8::MAX {
560            let bytes = [type_tag];
561
562            // An empty payload is currently invalid for all types except `OP_RETURN` and `P2TR` with an empty payload.
563            match type_tag {
564                OP_RETURN_TYPE_TAG => assert!(Descriptor::from_bytes(&bytes).is_ok()),
565                P2TR_TYPE_TAG => assert!(Descriptor::from_bytes(&bytes).is_ok()),
566                _ => assert!(Descriptor::from_bytes(&bytes).is_err()),
567            }
568        }
569
570        // Invalid type tag
571        let bytes = [5, 1, 2, 3, 4, 5, 6];
572        assert!(Descriptor::from_bytes(&bytes).is_err());
573
574        // Invalid payload length
575        // OP_RETURN with 100001 bytes (MAX_OP_RETURN_LEN + 1)
576        let mut bytes = vec![0; MAX_OP_RETURN_LEN + 2]; // 1 byte type tag + (MAX_OP_RETURN_LEN + 1) bytes payload
577        bytes[0] = 0; // OP_RETURN type tag
578        assert!(Descriptor::from_bytes(&bytes).is_err());
579
580        // P2PKH with 19 bytes
581        let bytes = [1; 20];
582        assert!(Descriptor::from_bytes(&bytes).is_err());
583
584        // P2TR with 33 bytes
585        let bytes = [4; 34];
586        assert!(Descriptor::from_bytes(&bytes).is_err());
587    }
588
589    #[test]
590    fn descriptor_to_bytes() {
591        let original: &[u8; 20] = &[
592            0, 99, 104, 97, 114, 108, 101, 121, 32, 108, 111, 118, 101, 115, 32, 104, 101, 105,
593            100, 105,
594        ];
595        let desc = Descriptor::from_str("00636861726c6579206c6f766573206865696469").unwrap();
596        let bytes = desc.to_bytes();
597        assert_eq!(bytes, original);
598    }
599
600    #[test]
601    fn descriptor_type() {
602        assert_eq!(DescriptorType::OpReturn.to_u8(), 0);
603        assert_eq!(DescriptorType::P2pkh.to_u8(), 1);
604        assert_eq!(DescriptorType::P2sh.to_u8(), 2);
605        assert_eq!(DescriptorType::P2wpkh.to_u8(), 3);
606        assert_eq!(DescriptorType::P2wsh.to_u8(), 3);
607        assert_eq!(DescriptorType::P2a.to_u8(), 4);
608        assert_eq!(DescriptorType::P2tr.to_u8(), 4);
609    }
610
611    #[test]
612    fn from_str() {
613        // OP_RETURN in hex string replacing the 6a (`OP_RETURN`)
614        // for a 0x00 (type_tag) byte for `OP_RETURN`.
615        // Source: https://bitcoin.stackexchange.com/a/29555
616        //         and transaction 8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684
617        let s = "00636861726c6579206c6f766573206865696469";
618        let desc = Descriptor::from_str(s).unwrap();
619        assert_eq!(desc.type_tag(), DescriptorType::OpReturn);
620        assert_eq!(desc.payload(), b"charley loves heidi");
621
622        // P2PKH
623        // Using 0x01 (type_tag) and a 20-byte hash
624        // Source: transaction 8bae12b5f4c088d940733dcd1455efc6a3a69cf9340e17a981286d3778615684
625        // Corresponds to address `1HnhWpkMHMjgt167kvgcPyurMmsCQ2WPgg`
626        let s = "01b8268ce4d481413c4e848ff353cd16104291c45b";
627        let desc = Descriptor::from_str(s).unwrap();
628        assert_eq!(desc.type_tag(), DescriptorType::P2pkh);
629        assert_eq!(
630            desc.payload(),
631            Vec::from_hex("b8268ce4d481413c4e848ff353cd16104291c45b").unwrap()
632        );
633
634        // P2SH
635        // Using 0x02 (type_tag) and a 20-byte hash
636        // Source: transaction a0f1aaa2fb4582c89e0511df0374a5a2833bf95f7314f4a51b55b7b71e90ce0f
637        // Corresponds to address `3CK4fEwbMP7heJarmU4eqA3sMbVJyEnU3V`
638        let s = "02748284390f9e263a4b766a75d0633c50426eb875";
639        let desc = Descriptor::from_str(s).unwrap();
640        assert_eq!(desc.type_tag(), DescriptorType::P2sh);
641        assert_eq!(
642            desc.payload(),
643            Vec::from_hex("748284390f9e263a4b766a75d0633c50426eb875").unwrap()
644        );
645
646        // P2WPKH
647        // Using 0x03 (type_tag) and a 20-byte hash
648        // Source: transaction 7c53ba0f1fc65f021749cac6a9c163e499fcb2e539b08c040802be55c33d32fe
649        // Corresponds to address `bc1qvugyzunmnq5y8alrmdrxnsh4gts9p9hmvhyd40`
650        let s = "03671041727b982843f7e3db4669c2f542e05096fb";
651        let desc = Descriptor::from_str(s).unwrap();
652        assert_eq!(desc.type_tag(), DescriptorType::P2wpkh);
653        assert_eq!(
654            desc.payload(),
655            Vec::from_hex("671041727b982843f7e3db4669c2f542e05096fb").unwrap()
656        );
657
658        // P2WSH
659        // Using 0x03 (type_tag) and a 32-byte hash
660        // Source: transaction fbf3517516ebdf03358a9ef8eb3569f96ac561c162524e37e9088eb13b228849
661        // Corresponds to address `bc1qvhu3557twysq2ldn6dut6rmaj3qk04p60h9l79wk4lzgy0ca8mfsnffz65`
662        let s = "0365f91a53cb7120057db3d378bd0f7d944167d43a7dcbff15d6afc4823f1d3ed3";
663        let desc = Descriptor::from_str(s).unwrap();
664        assert_eq!(desc.type_tag(), DescriptorType::P2wsh);
665        assert_eq!(
666            desc.payload(),
667            Vec::from_hex("65f91a53cb7120057db3d378bd0f7d944167d43a7dcbff15d6afc4823f1d3ed3")
668                .unwrap()
669        );
670
671        // P2A
672        // Using 0x04 (type_tag) and a 0-byte payload.
673        // Source: transaction c054743f0f3ecfac2cf08c40c7dd36fcb38928cf8e07d179693ca2692d041848
674        // Corresponds to address `bc1pfeesrawgf`
675        let s = "04";
676        let desc = Descriptor::from_str(s).unwrap();
677        assert_eq!(desc.type_tag(), DescriptorType::P2a);
678        assert_eq!(desc.payload(), Vec::from_hex("").unwrap());
679
680        // P2TR
681        // Using 0x04 (type_tag) and a 32-byte hash
682        // Source: transaction a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec
683        // Corresponds to address `bc1ppuxgmd6n4j73wdp688p08a8rte97dkn5n70r2ym6kgsw0v3c5ensrytduf`
684        let s = "040f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667";
685        let desc = Descriptor::from_str(s).unwrap();
686        assert_eq!(desc.type_tag(), DescriptorType::P2tr);
687        assert_eq!(
688            desc.payload(),
689            Vec::from_hex("0f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667")
690                .unwrap()
691        );
692    }
693
694    #[test]
695    fn to_string() {
696        let original = "00636861726c6579206c6f766573206865696469";
697        let desc = Descriptor::from_bytes(&[
698            0, 99, 104, 97, 114, 108, 101, 121, 32, 108, 111, 118, 101, 115, 32, 104, 101, 105,
699            100, 105,
700        ])
701        .unwrap();
702        let s = desc.to_string();
703        assert_eq!(s, original);
704    }
705
706    #[test]
707    fn invalid_from_str() {
708        // Invalid type tag
709        let s = "050000000000000000000000000000000000000000000000000000000000000000";
710        assert!(Descriptor::from_str(s).is_err());
711
712        // Invalid payload length
713        // OP_RETURN with 100001 bytes (create a hex string with (MAX_OP_RETURN_LEN + 1)*2 hex chars)
714        let s = "00".to_string() + &"00".repeat(MAX_OP_RETURN_LEN + 1);
715        assert!(Descriptor::from_str(&s).is_err());
716
717        // P2PKH with 19 bytes
718        let s = "0100000000000000000000000000000000000000";
719        assert!(Descriptor::from_str(s).is_err());
720
721        // P2A with 2 bytes
722        let s = "0400";
723        assert!(Descriptor::from_str(s).is_err());
724
725        // P2TR with 33 bytes
726        let s = "04000000000000000000000000000000000000000000000000000000000000000000";
727        assert!(Descriptor::from_str(s).is_err());
728    }
729
730    #[test]
731    fn test_p2a_fixed_bytes() {
732        let desc = Descriptor::from_str("04").unwrap();
733        let bytes = desc.to_fixed_payload_bytes::<P2A_LEN>();
734        assert_eq!(bytes.len(), P2A_LEN);
735    }
736
737    #[test]
738    fn test_p2tr_fixed_bytes() {
739        let desc = Descriptor::from_str(
740            "040f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667",
741        )
742        .unwrap();
743        let bytes = desc.to_fixed_payload_bytes::<P2TR_LEN>();
744        assert_eq!(bytes.len(), P2TR_LEN);
745    }
746
747    #[test]
748    fn test_p2pkh_fixed_bytes() {
749        let desc = Descriptor::from_str("01b8268ce4d481413c4e848ff353cd16104291c45b").unwrap();
750        let bytes = desc.to_fixed_payload_bytes::<P2PKH_LEN>();
751        assert_eq!(bytes.len(), P2PKH_LEN);
752    }
753
754    #[cfg(feature = "address")]
755    #[test]
756    fn invalid_new_p2tr() {
757        let invalid_payload = [0; P2TR_LEN];
758        let result = Descriptor::new_p2tr(&invalid_payload);
759        assert!(result.is_err());
760        assert_eq!(
761            result.err().unwrap(),
762            DescriptorError::InvalidXOnlyPublicKey
763        );
764    }
765}