dubp_documents_parser/raw_text/
wallet_script.rs

1use crate::*;
2
3pub fn wallet_script_from_str(source: &str) -> Result<WalletScriptV10, TextParseError> {
4    let mut pairs = RawDocumentsParser::parse(Rule::output_conds, source)
5        .map_err(|e| TextParseError::PestError(e.into()))?;
6    WalletScriptV10::from_pest_pair(pairs.next().unwrap_or_else(|| unreachable!()))
7    // get and unwrap the `output_conds` rule; never fails
8}
9
10impl FromPestPair for WalletScriptV10 {
11    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self, TextParseError> {
12        let mut pairs = pair.into_inner();
13        let term_left_pair = pairs.next().unwrap_or_else(|| unreachable!());
14
15        let mut nodes = SmallVec::new();
16
17        let term_left = parse_term(term_left_pair, &mut nodes);
18        let root = parse_op(term_left, pairs, &mut nodes);
19
20        Ok(WalletScriptV10 { root, nodes })
21    }
22}
23
24impl FromPestPair for WalletConditionV10 {
25    #[inline]
26    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self, TextParseError> {
27        Ok(match pair.as_rule() {
28            Rule::output_cond_sig => WalletConditionV10::Sig(
29                ed25519::PublicKey::from_base58(
30                    pair.into_inner()
31                        .next()
32                        .unwrap_or_else(|| unreachable!())
33                        .as_str(),
34                )
35                .unwrap_or_else(|_| unreachable!()),
36            ),
37            Rule::output_cond_xhx => WalletConditionV10::Xhx(
38                Hash::from_hex(
39                    pair.into_inner()
40                        .next()
41                        .unwrap_or_else(|| unreachable!())
42                        .as_str(),
43                )
44                .unwrap_or_else(|_| unreachable!()),
45            ),
46            Rule::output_cond_csv => WalletConditionV10::Csv(
47                pair.into_inner()
48                    .next()
49                    .unwrap_or_else(|| unreachable!())
50                    .as_str()
51                    .parse()
52                    .unwrap_or_else(|_| unreachable!()),
53            ),
54            Rule::output_cond_cltv => WalletConditionV10::Cltv(
55                pair.into_inner()
56                    .next()
57                    .unwrap_or_else(|| unreachable!())
58                    .as_str()
59                    .parse()
60                    .unwrap_or_else(|_| unreachable!()),
61            ),
62            r => panic!("unexpected rule: {:?}", r), // Grammar ensures that we never reach this line
63        })
64    }
65}
66
67#[inline]
68fn parse_term(pair: Pair<Rule>, nodes: &mut WalletScriptNodesV10) -> WalletSubScriptV10 {
69    match pair.as_rule() {
70        Rule::output_conds_brackets_expr => {
71            let mut pairs = pair.into_inner();
72            let term_left_pair = pairs.next().unwrap_or_else(|| unreachable!());
73            let term_left = parse_term(term_left_pair, nodes);
74            let sub_root = parse_op(term_left, pairs, nodes);
75            let sub_script = WalletSubScriptV10::Brackets(nodes.len());
76            nodes.push(sub_root);
77            sub_script
78        }
79        Rule::output_single_cond => WalletSubScriptV10::Single(
80            WalletConditionV10::from_pest_pair(
81                pair.into_inner().next().unwrap_or_else(|| unreachable!()),
82            )
83            .unwrap_or_else(|_| unreachable!()),
84        ),
85        r => panic!("unexpected rule: {:?}", r), // Grammar ensures that we never reach this line
86    }
87}
88
89fn parse_op(
90    left: WalletSubScriptV10,
91    mut pairs: Pairs<Rule>,
92    nodes: &mut WalletScriptNodesV10,
93) -> WalletSubScriptV10 {
94    if let Some(pair) = pairs.next() {
95        let left_index = nodes.len();
96        nodes.push(left);
97        let next_left_term = parse_term(pairs.next().unwrap_or_else(|| unreachable!()), nodes);
98        let right = parse_op(next_left_term, pairs, nodes);
99        let right_index = nodes.len();
100        nodes.push(right);
101        match pair.as_rule() {
102            Rule::output_cond_op_and => WalletSubScriptV10::And(left_index, right_index),
103            Rule::output_cond_op_or => WalletSubScriptV10::Or(left_index, right_index),
104            r => panic!("unexpected rule: {:?}", r), // Grammar ensures that we never reach this line
105        }
106    } else {
107        left
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::tests::*;
115
116    #[test]
117    fn parse_complex_wallet_script_v10() -> Result<(), TextParseError> {
118        let script_v10_str =
119            "SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || (XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA) && SIG(42jMJtb8chXrpHMAMcreVdyPJK7LtWjEeRqkPw4eSEVp))";
120        let expected_script = WalletScriptV10 {
121            root: WalletSubScriptV10::Or(0, 4),
122            nodes: svec![
123                WalletSubScriptV10::Single(WalletConditionV10::Sig(pk(
124                    "6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i"
125                ))),
126                WalletSubScriptV10::Single(WalletConditionV10::Xhx(h(
127                    "3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA"
128                ))),
129                WalletSubScriptV10::Single(WalletConditionV10::Sig(pk(
130                    "42jMJtb8chXrpHMAMcreVdyPJK7LtWjEeRqkPw4eSEVp"
131                ))),
132                WalletSubScriptV10::And(1, 2),
133                WalletSubScriptV10::Brackets(3),
134            ],
135        };
136
137        assert_eq!(script_v10_str, expected_script.to_string(),);
138
139        assert_eq!(expected_script, wallet_script_from_str(script_v10_str)?);
140
141        Ok(())
142    }
143}