Skip to main content

elements_miniscript/extensions/
tx_ver.rs

1//! Miniscript extension: ver_eq
2//! Note that this fragment is only supported for Segwit context
3//! You are most likely looking for taproot direct tx introspection
4
5use std::fmt;
6
7use elements::encode::serialize;
8
9use super::{FromTokenIterError, ParseableExt, TxEnv};
10use crate::descriptor::CovError;
11use crate::miniscript::astelem::StackCtxOperations;
12use crate::miniscript::lex::{Token as Tk, TokenIter};
13use crate::miniscript::satisfy::{Satisfaction, Witness};
14use crate::miniscript::types::extra_props::{OpLimits, TimelockInfo};
15use crate::miniscript::types::{Base, Correctness, Dissat, ExtData, Input, Malleability};
16use crate::policy::{self, Liftable};
17use crate::{
18    expression, interpreter, miniscript, util, Error, Extension, MiniscriptKey, Satisfier,
19    ToPublicKey,
20};
21
22/// Version struct
23/// `DEPTH <12> SUB PICK <num> EQUAL`
24#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
25pub struct LegacyVerEq {
26    /// the version of transaction
27    pub n: u32, // it's i32 in bitcoin core
28}
29
30impl fmt::Display for LegacyVerEq {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        write!(f, "ver_eq({})", self.n)
33    }
34}
35
36impl<Pk: MiniscriptKey> Liftable<Pk> for LegacyVerEq {
37    fn lift(&self) -> Result<policy::Semantic<Pk>, Error> {
38        Err(Error::CovError(CovError::CovenantLift))
39    }
40}
41
42impl Extension for LegacyVerEq {
43    fn segwit_ctx_checks(&self) -> Result<(), miniscript::context::ScriptContextError> {
44        Ok(())
45    }
46
47    fn corr_prop(&self) -> Correctness {
48        Correctness {
49            base: Base::B,
50            input: Input::Zero,
51            dissatisfiable: false, // No dissat from stack inputs
52            unit: true,
53        }
54    }
55
56    fn mall_prop(&self) -> Malleability {
57        Malleability {
58            dissat: Dissat::None, // No dissatisfactions from stack inputs
59            safe: false,
60            non_malleable: true,
61        }
62    }
63
64    fn extra_prop(&self) -> ExtData {
65        ExtData {
66            pk_cost: 4 + 1 + 1 + 4, // 4 opcodes, 1 push, (5) 4 byte push
67            has_free_verify: true,
68            stack_elem_count_sat: Some(0),
69            stack_elem_count_dissat: Some(0),
70            max_sat_size: Some((0, 0)),
71            max_dissat_size: Some((0, 0)),
72            timelock_info: TimelockInfo::default(),
73            exec_stack_elem_count_sat: Some(2),
74            exec_stack_elem_count_dissat: Some(2),
75            ops: OpLimits {
76                count: 4,
77                sat: Some(0),
78                nsat: Some(0),
79            },
80        }
81    }
82
83    fn script_size(&self) -> usize {
84        4 + 1 + 1 + 4 // opcodes + push opcodes + target size
85    }
86
87    fn from_name_tree(
88        name: &str,
89        children: &[expression::Tree<'_>],
90    ) -> Result<Self, FromTokenIterError> {
91        if children.len() == 1 && name == "ver_eq" {
92            let n = expression::terminal(&children[0], expression::parse_num)
93                .map_err(|_| FromTokenIterError)?;
94            Ok(Self { n })
95        } else {
96            // Correct error handling while parsing fromtree
97            Err(FromTokenIterError)
98        }
99    }
100}
101
102impl ParseableExt for LegacyVerEq {
103    fn satisfy<Pk, S>(&self, sat: &S) -> Satisfaction
104    where
105        Pk: ToPublicKey,
106        S: Satisfier<Pk>,
107    {
108        let wit = match sat.lookup_nversion() {
109            Some(k) => {
110                if k == self.n {
111                    Witness::empty()
112                } else {
113                    Witness::Impossible
114                }
115            }
116            // Note the unavailable instead of impossible because we don't know
117            // the version
118            None => Witness::Unavailable,
119        };
120        Satisfaction {
121            stack: wit,
122            has_sig: false,
123        }
124    }
125
126    fn dissatisfy<Pk, S>(&self, sat: &S) -> Satisfaction
127    where
128        Pk: ToPublicKey,
129        S: Satisfier<Pk>,
130    {
131        let wit = if let Some(k) = sat.lookup_nversion() {
132            if k == self.n {
133                Witness::Impossible
134            } else {
135                Witness::empty()
136            }
137        } else {
138            Witness::empty()
139        };
140        Satisfaction {
141            stack: wit,
142            has_sig: false,
143        }
144    }
145
146    fn push_to_builder(&self, builder: elements::script::Builder) -> elements::script::Builder {
147        builder.check_item_eq(12, &serialize(&self.n))
148    }
149
150    fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result<Self, FromTokenIterError> {
151        let ver = {
152            let sl = tokens.peek_slice(5).ok_or(FromTokenIterError)?;
153            if let Tk::PickPush4(ver) = sl[3] {
154                if sl[0] == Tk::Depth
155                    && sl[1] == Tk::Num(12)
156                    && sl[2] == Tk::Sub
157                    && sl[4] == Tk::Equal
158                {
159                    Self { n: ver }
160                } else {
161                    return Err(FromTokenIterError);
162                }
163            } else {
164                return Err(FromTokenIterError);
165            }
166        };
167        tokens.advance(5).expect("Size checked previously");
168        Ok(ver)
169    }
170
171    fn evaluate(
172        &self,
173        stack: &mut interpreter::Stack,
174        _txenv: Option<&TxEnv>,
175    ) -> Result<bool, interpreter::Error> {
176        // Version is at index 11
177        let ver = stack[11];
178        let elem = ver.try_push()?;
179        if elem.len() == 4 {
180            let wit_ver = util::slice_to_u32_le(elem);
181            if wit_ver == self.n {
182                stack.push(interpreter::Element::Satisfied);
183                Ok(true)
184            } else {
185                Ok(false)
186            }
187        } else {
188            Err(interpreter::Error::CovWitnessSizeErr {
189                pos: 1,
190                expected: 4,
191                actual: elem.len(),
192            })
193        }
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use bitcoin::PublicKey;
200
201    use super::*;
202    use crate::{Miniscript, Segwitv0};
203
204    #[test]
205    fn test_ver_eq() {
206        type MsExtVer = Miniscript<PublicKey, Segwitv0, LegacyVerEq>;
207
208        let ms = MsExtVer::from_str_insane("ver_eq(8)").unwrap();
209        // test string rtt
210        assert_eq!(ms.to_string(), "ver_eq(8)");
211        // script rtt
212        assert_eq!(ms, MsExtVer::parse_insane(&ms.encode()).unwrap())
213    }
214}