tinyminiscript/
descriptor.rs

1use core::marker::PhantomData;
2
3use crate::parser::{AST, ASTVisitor, Fragment, ParserContext, Position};
4
5/// Script descriptor
6#[cfg_attr(feature = "debug", derive(Debug))]
7#[derive(Clone, PartialEq)]
8pub enum Descriptor {
9    /// A raw scriptpubkey (including pay-to-pubkey) under Legacy context
10    Bare,
11
12    /// Pay-to-PubKey-Hash
13    Pkh,
14    /// Pay-to-ScriptHash(includes nested wsh/wpkh/sorted multi)
15    Sh,
16
17    /// Pay-to-Witness-PubKey-Hash
18    Wpkh,
19    /// Pay-to-Witness-ScriptHash with Segwitv0 context
20    Wsh,
21
22    /// Pay-to-Taproot
23    Tr,
24}
25
26impl Default for Descriptor {
27    #[inline]
28    fn default() -> Self {
29        Descriptor::Bare
30    }
31}
32
33impl Descriptor {
34    #[inline]
35    pub fn from_fragment(fragment: &str) -> Self {
36        match fragment {
37            "pkh" => Descriptor::Pkh,
38            "sh" => Descriptor::Sh,
39            "wpkh" => Descriptor::Wpkh,
40            "wsh" => Descriptor::Wsh,
41            "tr" => Descriptor::Tr,
42            _ => Descriptor::Bare,
43        }
44    }
45}
46
47pub struct DescriptorValidator<'a> {
48    phantom: PhantomData<&'a ()>,
49}
50
51impl<'a> DescriptorValidator<'a> {
52    #[inline]
53    pub fn new() -> Self {
54        Self {
55            phantom: PhantomData,
56        }
57    }
58}
59
60#[cfg_attr(feature = "debug", derive(Debug))]
61pub enum DescriptorVisitorError {
62    InvalidFragmentForDescriptor {
63        position: Position,
64        expected: Descriptor,
65        found: Descriptor,
66    },
67    PublicKeyNotCompressed {
68        position: Position,
69    },
70}
71
72impl<'a> ASTVisitor<'a, ()> for DescriptorValidator<'a> {
73    type Error = DescriptorVisitorError;
74
75    fn visit_ast(&mut self, ctx: &ParserContext<'a>, node: &AST<'a>) -> Result<(), Self::Error> {
76        match &node.fragment {
77            Fragment::Descriptor { descriptor, inner } => {
78                self.visit_ast_by_index(ctx, *inner)?;
79            }
80
81            Fragment::False => {}
82            Fragment::True => {}
83            Fragment::PkK { key } | Fragment::PkH { key } => match &ctx.inner_descriptor {
84                Descriptor::Bare => {}
85                Descriptor::Pkh => {}
86                Descriptor::Sh => {}
87                Descriptor::Wpkh | Descriptor::Wsh => {
88                    if !key.is_compressed() {
89                        return Err(DescriptorVisitorError::PublicKeyNotCompressed {
90                            position: node.position,
91                        });
92                    }
93                }
94                Descriptor::Tr => {}
95            },
96            Fragment::Older { n } => {}
97            Fragment::After { n } => {}
98            Fragment::Sha256 { h } => {}
99            Fragment::Hash256 { h } => {}
100            Fragment::Ripemd160 { h } => {}
101            Fragment::Hash160 { h } => {}
102            Fragment::AndOr { x, y, z } => {
103                self.visit_ast_by_index(ctx, *x)?;
104                self.visit_ast_by_index(ctx, *y)?;
105                self.visit_ast_by_index(ctx, *z)?;
106            }
107            Fragment::AndV { x, y } => {
108                self.visit_ast_by_index(ctx, *x)?;
109                self.visit_ast_by_index(ctx, *y)?;
110            }
111            Fragment::AndB { x, y } => {
112                self.visit_ast_by_index(ctx, *x)?;
113                self.visit_ast_by_index(ctx, *y)?;
114            }
115            Fragment::OrB { x, z } => {
116                self.visit_ast_by_index(ctx, *x)?;
117                self.visit_ast_by_index(ctx, *z)?;
118            }
119            Fragment::OrC { x, z } => {
120                self.visit_ast_by_index(ctx, *x)?;
121                self.visit_ast_by_index(ctx, *z)?;
122            }
123            Fragment::OrD { x, z } => {
124                self.visit_ast_by_index(ctx, *x)?;
125                self.visit_ast_by_index(ctx, *z)?;
126            }
127            Fragment::OrI { x, z } => {
128                self.visit_ast_by_index(ctx, *x)?;
129                self.visit_ast_by_index(ctx, *z)?;
130            }
131            Fragment::Thresh { k, xs } => {
132                for x in xs {
133                    self.visit_ast_by_index(ctx, *x)?;
134                }
135            }
136            Fragment::Multi { k, keys } => {
137                // (P2WSH only)
138                if ctx.inner_descriptor != Descriptor::Wsh {
139                    return Err(DescriptorVisitorError::InvalidFragmentForDescriptor {
140                        position: node.position,
141                        expected: Descriptor::Wsh,
142                        found: ctx.inner_descriptor.clone(),
143                    });
144                }
145            }
146            Fragment::MultiA { k, keys } => {
147                // Tapscript only
148                if ctx.inner_descriptor != Descriptor::Tr {
149                    return Err(DescriptorVisitorError::InvalidFragmentForDescriptor {
150                        position: node.position,
151                        expected: Descriptor::Tr,
152                        found: ctx.inner_descriptor.clone(),
153                    });
154                }
155            }
156            Fragment::Identity { identity_type, x } => {
157                self.visit_ast_by_index(ctx, *x)?;
158            }
159        }
160        Ok(())
161    }
162}