1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::*;

pub fn wallet_script_from_str(source: &str) -> Result<WalletScriptV10, RawTextParseError> {
    let mut pairs = RawDocumentsParser::parse(Rule::output_conds, source)
        .map_err(|e| RawTextParseError::PestError(e.into()))?;
    WalletScriptV10::from_pest_pair(unwrap!(pairs.next())) // get and unwrap the `output_conds` rule; never fails
}

impl FromPestPair for WalletScriptV10 {
    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self, RawTextParseError> {
        let mut pairs = pair.into_inner();
        let term_left_pair = unwrap!(pairs.next());

        let mut nodes = SmallVec::new();

        let term_left = parse_term(term_left_pair, &mut nodes);
        let root = parse_op(term_left, pairs, &mut nodes);

        Ok(WalletScriptV10 { root, nodes })
    }
}

impl FromPestPair for WalletConditionV10 {
    #[inline]
    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self, RawTextParseError> {
        Ok(match pair.as_rule() {
            Rule::output_cond_sig => WalletConditionV10::Sig(unwrap!(
                ed25519::PublicKey::from_base58(unwrap!(pair.into_inner().next()).as_str())
            )),
            Rule::output_cond_xhx => WalletConditionV10::Xhx(unwrap!(Hash::from_hex(
                unwrap!(pair.into_inner().next()).as_str()
            ))),
            Rule::output_cond_csv => {
                WalletConditionV10::Csv(unwrap!(unwrap!(pair.into_inner().next()).as_str().parse()))
            }
            Rule::output_cond_cltv => {
                WalletConditionV10::Cltv(unwrap!(unwrap!(pair.into_inner().next())
                    .as_str()
                    .parse()))
            }
            r => panic!("unexpected rule: {:?}", r), // Grammar ensures that we never reach this line
        })
    }
}

#[inline]
fn parse_term(pair: Pair<Rule>, nodes: &mut WalletScriptNodesV10) -> WalletSubScriptV10 {
    match pair.as_rule() {
        Rule::output_conds_brackets_expr => {
            let mut pairs = pair.into_inner();
            let term_left_pair = unwrap!(pairs.next());
            let term_left = parse_term(term_left_pair, nodes);
            let sub_root = parse_op(term_left, pairs, nodes);
            let sub_script = WalletSubScriptV10::Brackets(nodes.len());
            nodes.push(sub_root);
            sub_script
        }
        Rule::output_single_cond => {
            WalletSubScriptV10::Single(unwrap!(WalletConditionV10::from_pest_pair(unwrap!(pair
                .into_inner()
                .next()))))
        }
        r => panic!("unexpected rule: {:?}", r), // Grammar ensures that we never reach this line
    }
}

fn parse_op(
    left: WalletSubScriptV10,
    mut pairs: Pairs<Rule>,
    nodes: &mut WalletScriptNodesV10,
) -> WalletSubScriptV10 {
    if let Some(pair) = pairs.next() {
        let left_index = nodes.len();
        nodes.push(left);
        let next_left_term = parse_term(unwrap!(pairs.next()), nodes);
        let right = parse_op(next_left_term, pairs, nodes);
        let right_index = nodes.len();
        nodes.push(right);
        match pair.as_rule() {
            Rule::output_cond_op_and => WalletSubScriptV10::And(left_index, right_index),
            Rule::output_cond_op_or => WalletSubScriptV10::Or(left_index, right_index),
            r => panic!("unexpected rule: {:?}", r), // Grammar ensures that we never reach this line
        }
    } else {
        left
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::*;

    #[test]
    fn parse_complex_wallet_script_v10() -> Result<(), RawTextParseError> {
        let script_v10_str =
            "SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || (XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA) && SIG(42jMJtb8chXrpHMAMcreVdyPJK7LtWjEeRqkPw4eSEVp))";
        let expected_script = WalletScriptV10 {
            root: WalletSubScriptV10::Or(0, 4),
            nodes: svec![
                WalletSubScriptV10::Single(WalletConditionV10::Sig(pk(
                    "6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i"
                ))),
                WalletSubScriptV10::Single(WalletConditionV10::Xhx(h(
                    "3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA"
                ))),
                WalletSubScriptV10::Single(WalletConditionV10::Sig(pk(
                    "42jMJtb8chXrpHMAMcreVdyPJK7LtWjEeRqkPw4eSEVp"
                ))),
                WalletSubScriptV10::And(1, 2),
                WalletSubScriptV10::Brackets(3),
            ],
        };

        assert_eq!(script_v10_str, expected_script.to_string(),);

        assert_eq!(expected_script, wallet_script_from_str(script_v10_str)?);

        Ok(())
    }
}