Skip to main content

elements_miniscript/extensions/
introspect_ops.rs

1//! Miniscript Arithmetic expressions:
2//! Note that this fragment is only supported for Tapscript context
3use std::convert::TryFrom;
4use std::fmt;
5use std::str::FromStr;
6
7use bitcoin::hashes::{sha256, Hash};
8use bitcoin::hex::{DisplayHex, FromHex};
9use elements::address::Payload;
10use elements::confidential::Asset;
11use elements::opcodes::all::*;
12use elements::{confidential, encode, script, Address, AddressParams};
13
14use super::index_ops::IdxExpr;
15use super::param::{ExtParamTranslator, TranslateExtParam};
16use super::{ArgFromStr, CovExtArgs, EvalError, ExtParam, FromTokenIterError, ParseableExt, TxEnv};
17use crate::expression::{FromTree, Tree};
18use crate::miniscript::context::ScriptContextError;
19use crate::miniscript::lex::{Token as Tk, TokenIter};
20use crate::miniscript::satisfy::{Satisfaction, Witness};
21use crate::miniscript::types::extra_props::{OpLimits, TimelockInfo};
22use crate::miniscript::types::{Base, Correctness, Dissat, ExtData, Input, Malleability};
23use crate::{
24    expression, interpreter, script_num_size, Error, ExtTranslator, Extension, Satisfier,
25    ToPublicKey, TranslateExt,
26};
27
28/// Enum representing operations with transaction assets.
29/// Every variant of this enum pushes a 32 byte asset + 1 byte prefix on stack top..
30/// These operations also support confidential assets.
31/// This will abort when
32///     - Supplied index is out of bounds.
33#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
34pub enum AssetExpr<T: ExtParam> {
35    /* leaf fragments/terminals */
36    /// A constant asset id
37    /// Minimal push of this `<asset_id>`
38    Const(T),
39    /// Asset under the current executing input
40    /// `INSPECTCURRENTINPUTINDEX INPSECTINPUTASSET`
41    CurrInputAsset,
42    /// Explicit asset at the given input index
43    /// `i INPSECTINPUTASSET`
44    Input(IdxExpr),
45    /// Explicit asset at the given output index
46    /// `i INPSECTOUTPUTASSET`
47    Output(IdxExpr),
48}
49
50/// Enum representing operations with transaction values.
51/// Every variant of this enum pushes a 32 byte value + 1 byte prefix on stack top..
52/// These operations also support confidential values.
53/// This will abort when
54///     - Supplied index is out of bounds.
55#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
56pub enum ValueExpr<T: ExtParam> {
57    /* leaf fragments/terminals */
58    /// A constant Value
59    Const(T),
60    /// Value under the current executing input
61    /// `INSPECTCURRENTINPUTINDEX INPSECTINPUTVALUE`
62    CurrInputValue,
63    ///  Value(possibly confidential) at the given input index
64    /// `i INPSECTINPUTVALUE`
65    Input(IdxExpr),
66    /// Value(possibly confidential) at the given output index
67    /// `i INPSECTOUTPUTVALUE`
68    Output(IdxExpr),
69}
70
71/// Enum representing operations with transaction script pubkeys.
72/// Every variant of this enum pushes a witness program + 1 byte witness version on stack top.
73/// If the script pubkey is not a witness program. Push a sha256 hash of the
74/// script pubkey followed by -1 witness version
75/// This will abort when
76///     - Supplied index is out of bounds.
77#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
78pub enum SpkExpr<T: ExtParam> {
79    /* leaf fragments/terminals */
80    /// A constant fixed script pubkey
81    /// Pushes the witness program followed by witness version
82    /// Pushes -1 if the legacy script pubkeys followed by sha256 hash of script pubkey
83    Const(T),
84    /// Script pubkey under the current executing input
85    /// `INSPECTCURRENTINPUTINDEX INPSECTINPUTSCRIPTPUBKEY`
86    CurrInputSpk,
87    /// Explicit asset at the given input index
88    /// `i INPSECTINPUTSCRIPTPUBKEY`
89    Input(IdxExpr),
90    /// Explicit asset at the given output index
91    /// `i INPSECTOUTPUTSCRIPTPUBKEY`
92    Output(IdxExpr),
93}
94
95/// Miniscript Fragment containing arith expressions
96/// Expr cannot be directly used a miniscript fragment because it pushes a 64 bit
97/// value on stack. Two expressions can be combined with Arith to something is
98/// of Base type B to be used in miniscript expressions
99#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
100pub enum CovOps<T: ExtParam> {
101    /// Checks that asset is explicit
102    /// `[X] <1> EQUAL NIP`
103    IsExpAsset(AssetExpr<T>),
104    /// Checks if the value is explicit
105    /// `[X] <1> EQUAL NIP`
106    /// The script translation is same as that of IsExpAsset, but the data structure
107    /// distinguishes them for clarity.
108    IsExpValue(ValueExpr<T>),
109    /// Checks that both assets are equal (maybe confidential)
110    /// `[X] TOALTSTACK [Y] FROMALTSTACK EQUAL TOALTSTACK EQUAL FROMALTSTACK BOOLAND`
111    AssetEq(AssetExpr<T>, AssetExpr<T>),
112    /// Checks that both values are equal (maybe confidential)
113    /// `[X] TOALTSTACK [Y] FROMALTSTACK EQUAL TOALTSTACK EQUAL FROMALTSTACK BOOLAND`
114    ValueEq(ValueExpr<T>, ValueExpr<T>),
115    /// Script pubkey equal. Checks the witness version and program. Also works for
116    /// legacy programs.
117    /// `[X] TOALTSTACK [Y] FROMALTSTACK EQUAL TOALTSTACK EQUAL FROMALTSTACK BOOLAND`
118    SpkEq(SpkExpr<T>, SpkExpr<T>),
119    /// Current input index equality
120    /// `<i> PUSHCURRENTINPUTINDEX EQUAL`
121    CurrIndEq(usize),
122    /// Index equality
123    /// `[X] [Y] EQUAL`
124    IdxEq(IdxExpr, IdxExpr),
125}
126
127impl<T: ExtParam> AssetExpr<T> {
128    /// Returns the script size of this [`AssetExpr<T>`].
129    fn script_size(&self) -> usize {
130        match self {
131            AssetExpr::Const(_) => 33 + 1,
132            AssetExpr::CurrInputAsset => 2,
133            AssetExpr::Input(i) => i.script_size() + 1,
134            AssetExpr::Output(i) => i.script_size() + 1,
135        }
136    }
137
138    /// Returns the extention translation from AssetExpr<T> to AssetExpr<Q>
139    fn _translate_ext<Q, E, Ext>(&self, t: &mut Ext) -> Result<AssetExpr<Q>, E>
140    where
141        Ext: ExtParamTranslator<T, Q, E>,
142        Q: ExtParam,
143    {
144        let res = match self {
145            AssetExpr::Const(c) => AssetExpr::Const(t.ext(c)?),
146            AssetExpr::CurrInputAsset => AssetExpr::CurrInputAsset,
147            AssetExpr::Input(i) => AssetExpr::Input(i.clone()),
148            AssetExpr::Output(i) => AssetExpr::Output(i.clone()),
149        };
150        Ok(res)
151    }
152}
153
154impl<T: ExtParam> fmt::Display for AssetExpr<T> {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match self {
157            AssetExpr::Const(asset) => write!(f, "{}", asset),
158            AssetExpr::CurrInputAsset => write!(f, "curr_inp_asset"),
159            AssetExpr::Input(i) => write!(f, "inp_asset({})", i),
160            AssetExpr::Output(i) => write!(f, "out_asset({})", i),
161        }
162    }
163}
164
165impl<T: ExtParam> fmt::Debug for AssetExpr<T> {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        match self {
168            AssetExpr::Const(asset) => write!(f, "{:?}", asset),
169            AssetExpr::CurrInputAsset => write!(f, "curr_inp_asset"),
170            AssetExpr::Input(i) => write!(f, "inp_asset({:?})", i),
171            AssetExpr::Output(i) => write!(f, "out_asset({:?})", i),
172        }
173    }
174}
175
176impl<T: ExtParam> ArgFromStr for AssetExpr<T> {
177    fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
178        let top = expression::Tree::from_str(s)?;
179        Self::from_tree_parent(&top, parent, pos)
180    }
181}
182
183impl<T: ExtParam> AssetExpr<T> {
184    fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
185        match (top.name, top.args.len()) {
186            ("curr_inp_asset", 0) => Ok(AssetExpr::CurrInputAsset),
187            ("inp_asset", 1) => expression::unary(top, AssetExpr::Input),
188            ("out_asset", 1) => expression::unary(top, AssetExpr::Output),
189            (asset, 0) => Ok(AssetExpr::Const(T::arg_from_str(asset, parent, pos)?)),
190            _ => Err(Error::Unexpected(format!(
191                "{}({} args) while parsing Extension",
192                top.name,
193                top.args.len(),
194            ))),
195        }
196    }
197}
198
199impl<T: ExtParam> ValueExpr<T> {
200    /// Returns the script size of this [`ValueExpr<T>`].
201    fn script_size(&self) -> usize {
202        match self {
203            ValueExpr::Const(_c) => 33 + 1, // Worst case size for fee estimation
204            ValueExpr::CurrInputValue => 2,
205            ValueExpr::Input(i) => i.script_size() + 1,
206            ValueExpr::Output(i) => i.script_size() + 1,
207        }
208    }
209
210    /// Returns the extention translation from ValueExpr<T> to ValueExpr<Q>
211    fn _translate_ext<Q, E, Ext>(&self, t: &mut Ext) -> Result<ValueExpr<Q>, E>
212    where
213        Ext: ExtParamTranslator<T, Q, E>,
214        Q: ExtParam,
215    {
216        let res = match self {
217            ValueExpr::Const(c) => ValueExpr::Const(t.ext(c)?),
218            ValueExpr::CurrInputValue => ValueExpr::CurrInputValue,
219            ValueExpr::Input(i) => ValueExpr::Input(i.clone()),
220            ValueExpr::Output(i) => ValueExpr::Output(i.clone()),
221        };
222        Ok(res)
223    }
224}
225
226impl<T: ExtParam> fmt::Display for ValueExpr<T> {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        match self {
229            ValueExpr::Const(asset) => write!(f, "{}", asset),
230            ValueExpr::CurrInputValue => write!(f, "curr_inp_value"),
231            ValueExpr::Input(i) => write!(f, "inp_value({})", i),
232            ValueExpr::Output(i) => write!(f, "out_value({})", i),
233        }
234    }
235}
236
237impl<T: ExtParam> fmt::Debug for ValueExpr<T> {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        match self {
240            ValueExpr::Const(asset) => write!(f, "{:?}", asset),
241            ValueExpr::CurrInputValue => write!(f, "curr_inp_value"),
242            ValueExpr::Input(i) => write!(f, "inp_value({:?})", i),
243            ValueExpr::Output(i) => write!(f, "out_value({:?})", i),
244        }
245    }
246}
247
248impl<T: ExtParam> ArgFromStr for ValueExpr<T> {
249    fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
250        let top = expression::Tree::from_str(s)?;
251        Self::from_tree_parent(&top, parent, pos)
252    }
253}
254
255impl<T: ExtParam> ValueExpr<T> {
256    fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
257        match (top.name, top.args.len()) {
258            ("curr_inp_value", 0) => Ok(ValueExpr::CurrInputValue),
259            ("inp_value", 1) => expression::unary(top, ValueExpr::Input),
260            ("out_value", 1) => expression::unary(top, ValueExpr::Output),
261            (value, 0) => Ok(ValueExpr::Const(T::arg_from_str(value, parent, pos)?)),
262            _ => Err(Error::Unexpected(format!(
263                "{}({} args) while parsing Extension",
264                top.name,
265                top.args.len(),
266            ))),
267        }
268    }
269}
270
271impl<T: ExtParam> SpkExpr<T> {
272    /// Returns the script size of this [`SpkExpr<T>`].
273    fn script_size(&self) -> usize {
274        match self {
275            SpkExpr::Const(_c) => 32 + 1 + 1,
276            SpkExpr::CurrInputSpk => 2,
277            SpkExpr::Input(i) => i.script_size() + 1,
278            SpkExpr::Output(i) => i.script_size() + 1,
279        }
280    }
281
282    /// Returns the extention translation from SpkExpr<T> to SpkExpr<Q>
283    fn _translate_ext<Q, E, Ext>(&self, t: &mut Ext) -> Result<SpkExpr<Q>, E>
284    where
285        Ext: ExtParamTranslator<T, Q, E>,
286        Q: ExtParam,
287    {
288        let res = match self {
289            SpkExpr::Const(c) => SpkExpr::Const(t.ext(c)?),
290            SpkExpr::CurrInputSpk => SpkExpr::CurrInputSpk,
291            SpkExpr::Input(i) => SpkExpr::Input(i.clone()),
292            SpkExpr::Output(i) => SpkExpr::Output(i.clone()),
293        };
294        Ok(res)
295    }
296}
297
298impl<T: ExtParam> fmt::Display for SpkExpr<T> {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        match self {
301            SpkExpr::Const(asset) => write!(f, "{}", asset),
302            SpkExpr::CurrInputSpk => write!(f, "curr_inp_spk"),
303            SpkExpr::Input(i) => write!(f, "inp_spk({})", i),
304            SpkExpr::Output(i) => write!(f, "out_spk({})", i),
305        }
306    }
307}
308
309impl<T: ExtParam> fmt::Debug for SpkExpr<T> {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        match self {
312            SpkExpr::Const(asset) => write!(f, "{:?}", asset),
313            SpkExpr::CurrInputSpk => write!(f, "curr_inp_spk"),
314            SpkExpr::Input(i) => write!(f, "inp_spk({:?})", i),
315            SpkExpr::Output(i) => write!(f, "out_spk({:?})", i),
316        }
317    }
318}
319
320impl<T: ExtParam> ArgFromStr for SpkExpr<T> {
321    fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
322        let top = expression::Tree::from_str(s)?;
323        Self::from_tree_parent(&top, parent, pos)
324    }
325}
326
327impl<T: ExtParam> SpkExpr<T> {
328    fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result<Self, Error> {
329        match (top.name, top.args.len()) {
330            ("curr_inp_spk", 0) => Ok(SpkExpr::CurrInputSpk),
331            ("inp_spk", 1) => expression::unary(top, SpkExpr::Input),
332            ("out_spk", 1) => expression::unary(top, SpkExpr::Output),
333            (asset, 0) => Ok(SpkExpr::Const(T::arg_from_str(asset, parent, pos)?)),
334            _ => Err(Error::Unexpected(format!(
335                "{}({} args) while parsing Extension",
336                top.name,
337                top.args.len(),
338            ))),
339        }
340    }
341}
342
343impl<T: ExtParam> fmt::Display for CovOps<T> {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        match self {
346            CovOps::IsExpAsset(a) => write!(f, "is_exp_asset({})", a),
347            CovOps::IsExpValue(v) => write!(f, "is_exp_value({})", v),
348            CovOps::AssetEq(a, b) => write!(f, "asset_eq({},{})", a, b),
349            CovOps::ValueEq(a, b) => write!(f, "value_eq({},{})", a, b),
350            CovOps::SpkEq(a, b) => write!(f, "spk_eq({},{})", a, b),
351            CovOps::CurrIndEq(i) => write!(f, "curr_idx_eq({})", i),
352            CovOps::IdxEq(a, b) => write!(f, "idx_eq({},{})", a, b),
353        }
354    }
355}
356
357impl<T: ExtParam> fmt::Debug for CovOps<T> {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        match self {
360            CovOps::IsExpAsset(a) => write!(f, "is_exp_asset({:?})", a),
361            CovOps::IsExpValue(v) => write!(f, "is_exp_value({:?})", v),
362            CovOps::AssetEq(a, b) => write!(f, "asset_eq({:?},{:?})", a, b),
363            CovOps::ValueEq(a, b) => write!(f, "value_eq({:?},{:?})", a, b),
364            CovOps::SpkEq(a, b) => write!(f, "spk_eq({:?},{:?})", a, b),
365            CovOps::CurrIndEq(i) => write!(f, "curr_idx_eq({:?})", i),
366            CovOps::IdxEq(a, b) => write!(f, "idx_eq({},{})", a, b),
367        }
368    }
369}
370
371impl<T: ExtParam> FromStr for CovOps<T> {
372    type Err = Error;
373
374    fn from_str(s: &str) -> Result<Self, Self::Err> {
375        let top = expression::Tree::from_str(s)?;
376        Self::from_tree(&top)
377    }
378}
379
380impl<T: ExtParam> FromTree for CovOps<T> {
381    fn from_tree(top: &Tree<'_>) -> Result<Self, Error> {
382        match (top.name, top.args.len()) {
383            ("is_exp_asset", 1) => {
384                AssetExpr::from_tree_parent(&top.args[0], top.name, 0).map(CovOps::IsExpAsset)
385            }
386            ("is_exp_value", 1) => {
387                ValueExpr::from_tree_parent(&top.args[0], top.name, 0).map(CovOps::IsExpValue)
388            }
389            ("asset_eq", 2) => {
390                let l = AssetExpr::from_tree_parent(&top.args[0], top.name, 0)?;
391                let r = AssetExpr::from_tree_parent(&top.args[1], top.name, 1)?;
392                Ok(CovOps::AssetEq(l, r))
393            }
394            ("value_eq", 2) => {
395                let l = ValueExpr::from_tree_parent(&top.args[0], top.name, 0)?;
396                let r = ValueExpr::from_tree_parent(&top.args[1], top.name, 1)?;
397                Ok(CovOps::ValueEq(l, r))
398            }
399            ("spk_eq", 2) => {
400                let l = SpkExpr::from_tree_parent(&top.args[0], top.name, 0)?;
401                let r = SpkExpr::from_tree_parent(&top.args[1], top.name, 1)?;
402                Ok(CovOps::SpkEq(l, r))
403            }
404            ("curr_idx_eq", 1) => {
405                expression::terminal(&top.args[0], expression::parse_num::<usize>)
406                    .map(CovOps::CurrIndEq)
407            }
408            ("idx_eq", 2) => {
409                let l = IdxExpr::from_tree(&top.args[0])?;
410                let r = IdxExpr::from_tree(&top.args[1])?;
411                Ok(CovOps::IdxEq(l, r))
412            }
413            _ => Err(Error::Unexpected(format!(
414                "{}({} args) while parsing Extension",
415                top.name,
416                top.args.len(),
417            ))),
418        }
419    }
420}
421
422impl<T: ExtParam> Extension for CovOps<T> {
423    fn corr_prop(&self) -> Correctness {
424        Correctness {
425            base: Base::B,
426            input: Input::Zero,    // No input from stack
427            dissatisfiable: false, // No dissatisfactions possible from stack inputs
428            unit: true,
429        }
430    }
431
432    fn mall_prop(&self) -> Malleability {
433        Malleability {
434            dissat: Dissat::None, // No dissatisfactions from stack inputs
435            safe: false,          // Unsafe as a top fragment
436            non_malleable: true, // Script satisfaction is non-malleable, whole fragment tx could be malleable
437        }
438    }
439
440    fn extra_prop(&self) -> ExtData {
441        ExtData {
442            pk_cost: self.script_size(), // 1 opcodes, 1 key push, msg, 1 msg push
443            has_free_verify: matches!(self, CovOps::CurrIndEq(..)),
444            stack_elem_count_sat: Some(0),
445            stack_elem_count_dissat: Some(0),
446            max_sat_size: Some((0, 0)),
447            max_dissat_size: Some((0, 0)),
448            timelock_info: TimelockInfo::default(),
449            exec_stack_elem_count_sat: Some(4), // There is composition in asset/value/spk expressions. Only max 4 depth with asset expressions
450            exec_stack_elem_count_dissat: Some(4),
451            ops: OpLimits {
452                // Opcodes are really not relevant in tapscript as BIP342 removes all rules on them
453                // So, don't make any effort in trying to compute and cache them.
454                count: 0,
455                sat: Some(0),
456                nsat: Some(0),
457            },
458        }
459    }
460
461    fn script_size(&self) -> usize {
462        match self {
463            CovOps::IsExpAsset(a) => a.script_size() + 3,
464            CovOps::IsExpValue(v) => v.script_size() + 3,
465            CovOps::AssetEq(a, b) => a.script_size() + b.script_size() + 7,
466            CovOps::ValueEq(a, b) => a.script_size() + b.script_size() + 7,
467            CovOps::SpkEq(a, b) => a.script_size() + b.script_size() + 7,
468            CovOps::CurrIndEq(i) => script_num_size(*i) + 2,
469            CovOps::IdxEq(a, b) => a.script_size() + b.script_size() + 1,
470        }
471    }
472
473    fn from_name_tree(name: &str, children: &[Tree<'_>]) -> Result<Self, FromTokenIterError> {
474        let tree = Tree {
475            name,
476            args: children.to_vec(), // Cloning references here, it is possible to avoid the to_vec() here,
477                                     // but it requires lot of refactor.
478        };
479        Self::from_tree(&tree).map_err(|_| FromTokenIterError)
480    }
481
482    fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> {
483        // New opcodes only supported in taproot context
484        Err(ScriptContextError::ExtensionError(
485            "Introspection opcodes only available in Taproot".to_string(),
486        ))
487    }
488}
489
490impl<PArg, QArg> TranslateExt<CovOps<PArg>, CovOps<QArg>> for CovOps<PArg>
491where
492    CovOps<PArg>: Extension,
493    CovOps<QArg>: Extension,
494    PArg: ExtParam,
495    QArg: ExtParam,
496{
497    type Output = CovOps<QArg>;
498
499    fn translate_ext<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
500    where
501        T: ExtTranslator<CovOps<PArg>, CovOps<QArg>, E>,
502    {
503        t.ext(self)
504    }
505}
506
507// Use ExtParamTranslator as a ExtTranslator
508impl<T, PArg, QArg, E> ExtTranslator<CovOps<PArg>, CovOps<QArg>, E> for T
509where
510    T: ExtParamTranslator<PArg, QArg, E>,
511    PArg: ExtParam,
512    QArg: ExtParam,
513{
514    /// Translates one extension to another
515    fn ext(&mut self, cov_ops: &CovOps<PArg>) -> Result<CovOps<QArg>, E> {
516        TranslateExtParam::translate_ext(cov_ops, self)
517    }
518}
519
520/// Wrapper around [`elements::Script`] for representing script pubkeys
521// Required because the fmt::Display of elements::Script does not print hex
522#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
523pub struct Spk(SpkInner);
524
525impl Spk {
526    /// Creates a new [`Spk`].
527    pub fn new(s: elements::Script) -> Self {
528        Spk(SpkInner::Script(s))
529    }
530}
531
532/// Script pubkey representing either a known script or a hash of legacy script
533#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
534pub enum SpkInner {
535    /// A complete known script
536    Script(elements::Script),
537    /// An hashed legacy script pubkey
538    Hashed(sha256::Hash),
539}
540
541impl fmt::Display for Spk {
542    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543        match &self.0 {
544            SpkInner::Script(s) => write!(f, "{}", s.as_bytes().as_hex()),
545            SpkInner::Hashed(_h) => write!(f, "hashed_spk"), // This should never be used
546        }
547    }
548}
549
550impl ArgFromStr for Spk {
551    fn arg_from_str(s: &str, parent: &str, _pos: usize) -> Result<Self, Error> {
552        if parent != "spk_eq" {
553            return Err(Error::Unexpected(
554                "spk expressions can only used in spk_eq".to_string(),
555            ));
556        }
557        let inner = elements::Script::from_hex_no_prefix(s)
558            .map_err(|e| Error::Unexpected(e.to_string()))?;
559        Ok(Spk::new(inner))
560    }
561}
562
563impl ArgFromStr for confidential::Asset {
564    fn arg_from_str(s: &str, parent: &str, _pos: usize) -> Result<Self, Error> {
565        if parent != "asset_eq" && parent != "is_exp_asset" {
566            return Err(Error::Unexpected(
567                "asset expressions only allowed inside asset_eq and is_exp_asset".to_string(),
568            ));
569        }
570        let asset_hex = Vec::<u8>::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?;
571        elements::encode::deserialize(&asset_hex).map_err(|e| Error::Unexpected(e.to_string()))
572    }
573}
574
575impl ArgFromStr for confidential::Value {
576    fn arg_from_str(s: &str, parent: &str, _pos: usize) -> Result<Self, Error> {
577        if parent != "value_eq" && parent != "is_exp_value" {
578            return Err(Error::Unexpected(
579                "value expressions only allowed inside value_eq and is_exp_value".to_string(),
580            ));
581        }
582        let asset_hex = Vec::<u8>::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?;
583        elements::encode::deserialize(&asset_hex).map_err(|e| Error::Unexpected(e.to_string()))
584    }
585}
586
587// Internal helper function to construct asset from prefix and commitments
588fn asset(pref: u8, comm: &[u8]) -> Option<confidential::Asset> {
589    let mut bytes = [0u8; 33];
590    bytes[0] = pref;
591    if comm.len() != 32 {
592        return None;
593    }
594    bytes[1..].copy_from_slice(comm);
595    encode::deserialize(&bytes).ok()
596}
597
598// Internal helper function to construct asset from prefix and components
599fn value(pref: u8, comm: &[u8]) -> Option<confidential::Value> {
600    if comm.len() == 32 {
601        let mut bytes = [0u8; 33];
602        bytes[0] = pref;
603        bytes[1..].copy_from_slice(comm);
604        encode::deserialize(&bytes).ok()
605    } else if comm.len() == 8 {
606        let mut bytes = [0u8; 8];
607        bytes.copy_from_slice(comm);
608        if pref == 1 {
609            Some(confidential::Value::Explicit(u64::from_le_bytes(bytes)))
610        } else {
611            None
612        }
613    } else {
614        None
615    }
616}
617
618// Internal helper function to construct script pubkey from prefix and components
619fn spk(pref: i8, prog: &[u8]) -> Option<elements::Script> {
620    if pref == -1 {
621        // Cannot infer script pubkey from sha256::Hash
622        // In future, we can add fragments to check against certain hard-coded spks like fee spk.
623        None
624    } else if pref <= 16 && pref >= 0 {
625        Some(
626            script::Builder::new()
627                .push_int(i64::from(pref))
628                .push_slice(prog)
629                .into_script(),
630        )
631    } else {
632        None
633    }
634}
635
636// Internal function to convert a script pubkey into (witness version, program)
637// This converts legacy programs to (-1, sha256::Hash(spk))
638fn spk_to_components(s: &elements::Script) -> (i8, Vec<u8>) {
639    if !s.is_witness_program() {
640        (
641            -1,
642            sha256::Hash::hash(s.as_bytes()).to_byte_array().to_vec(),
643        )
644    } else {
645        // indirect way to get payload.
646        // The address parameters don't really matter here
647        let addr = Address::from_script(s, None, &AddressParams::ELEMENTS).unwrap();
648        if let Payload::WitnessProgram { version, program } = addr.payload {
649            (version.to_u8() as i8, program)
650        } else {
651            unreachable!("All witness programs have well defined payload")
652        }
653    }
654}
655
656impl AssetExpr<CovExtArgs> {
657    /// Push this script to builder
658    /// Panics when trying to push a Null asset. This never occur in honest use-cases
659    /// as there is no such thing as Null asset
660    pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
661        match self {
662            AssetExpr::Const(CovExtArgs::Asset(a)) => {
663                match a {
664                    Asset::Null => unreachable!("Attempt to push Null asset"),
665                    Asset::Explicit(a) => builder.push_slice(a.as_byte_array()).push_int(1), // explicit prefix
666                    Asset::Confidential(c) => {
667                        let ser = c.serialize();
668                        builder.push_slice(&ser[1..]).push_int(i64::from(ser[0]))
669                    }
670                }
671            }
672            AssetExpr::Const(_) => unreachable!(
673                "Both constructors from_str and from_token_iter
674            check that the correct variant is used in asset"
675            ),
676            AssetExpr::CurrInputAsset => builder
677                .push_opcode(OP_PUSHCURRENTINPUTINDEX)
678                .push_opcode(OP_INSPECTINPUTASSET),
679            AssetExpr::Input(i) => i.push_to_builder(builder).push_opcode(OP_INSPECTINPUTASSET),
680            AssetExpr::Output(i) => i
681                .push_to_builder(builder)
682                .push_opcode(OP_INSPECTOUTPUTASSET),
683        }
684    }
685
686    /// Evaluate this expression
687    pub fn eval(&self, env: &TxEnv) -> Result<confidential::Asset, EvalError> {
688        match self {
689            AssetExpr::Const(CovExtArgs::Asset(a)) => Ok(*a),
690            AssetExpr::Const(_) => unreachable!(
691                "Both constructors from_str and from_token_iter
692            check that the correct variant is used in asset"
693            ),
694            AssetExpr::CurrInputAsset => {
695                if env.idx() >= env.spent_utxos().len() {
696                    return Err(EvalError::UtxoIndexOutOfBounds(
697                        env.idx(),
698                        env.spent_utxos().len(),
699                    ));
700                }
701                Ok(env.spent_utxos()[env.idx()].asset)
702            }
703            AssetExpr::Input(i) => {
704                let i = i.eval(env)?;
705                if i >= env.spent_utxos().len() {
706                    return Err(EvalError::UtxoIndexOutOfBounds(i, env.spent_utxos().len()));
707                }
708                Ok(env.spent_utxos()[i].asset)
709            }
710            AssetExpr::Output(i) => {
711                let i = i.eval(env)?;
712                if i >= env.tx().output.len() {
713                    return Err(EvalError::OutputIndexOutOfBounds(i, env.tx().output.len()));
714                }
715                Ok(env.tx().output[i].asset)
716            }
717        }
718    }
719
720    /// Returns (self, start_pos) parsed reversed form tokens starting with index end_pos
721    /// Expression is parsed from tokens`[start:end_pos]`
722    pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
723        let tks = tokens;
724        let e = end_pos; // short abbreviations for succinct readable code
725        if let Some(&[Tk::Bytes32(asset_comm), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
726            let asset = asset(u8::try_from(i).ok()?, asset_comm)?;
727            Some((AssetExpr::Const(CovExtArgs::Asset(asset)), e - 2))
728        } else if let Some(&[Tk::CurrInp, Tk::InpAsset]) = tks.get(e.checked_sub(2)?..e) {
729            Some((AssetExpr::CurrInputAsset, e - 2))
730        } else if let Some(&[Tk::InpAsset]) = tks.get(e.checked_sub(1)?..e) {
731            let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
732            Some((AssetExpr::Input(idx_expr), e))
733        } else if let Some(&[Tk::OutAsset]) = tks.get(e.checked_sub(1)?..e) {
734            let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
735            Some((AssetExpr::Output(idx_expr), e))
736        } else {
737            None
738        }
739    }
740}
741
742impl ValueExpr<CovExtArgs> {
743    /// Push this script to builder
744    pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
745        match self {
746            ValueExpr::Const(CovExtArgs::Value(a)) => {
747                match a {
748                    confidential::Value::Null => {
749                        builder.push_slice(&0i64.to_le_bytes()).push_int(1)
750                    } // null amounts are 0 values
751                    confidential::Value::Explicit(a) => {
752                        builder.push_slice(&a.to_le_bytes()).push_int(1)
753                    } // explicit prefix
754                    confidential::Value::Confidential(c) => {
755                        let ser = c.serialize();
756                        builder.push_slice(&ser[1..]).push_int(i64::from(ser[0]))
757                    }
758                }
759            }
760            ValueExpr::Const(_) => unreachable!(
761                "Both constructors from_str and from_token_iter
762            check that the correct variant is used in Value"
763            ),
764            ValueExpr::CurrInputValue => builder
765                .push_opcode(OP_PUSHCURRENTINPUTINDEX)
766                .push_opcode(OP_INSPECTINPUTVALUE),
767            ValueExpr::Input(i) => i.push_to_builder(builder).push_opcode(OP_INSPECTINPUTVALUE),
768            ValueExpr::Output(i) => i
769                .push_to_builder(builder)
770                .push_opcode(OP_INSPECTOUTPUTVALUE),
771        }
772    }
773
774    /// Evaluate this expression
775    pub fn eval(&self, env: &TxEnv) -> Result<confidential::Value, EvalError> {
776        match self {
777            ValueExpr::Const(CovExtArgs::Value(a)) => Ok(*a),
778            ValueExpr::Const(_) => unreachable!(
779                "Both constructors from_str and from_token_iter
780            check that the correct variant is used in Value"
781            ),
782            ValueExpr::CurrInputValue => {
783                if env.idx() >= env.spent_utxos().len() {
784                    return Err(EvalError::UtxoIndexOutOfBounds(
785                        env.idx(),
786                        env.spent_utxos().len(),
787                    ));
788                }
789                Ok(env.spent_utxos()[env.idx()].value)
790            }
791            ValueExpr::Input(i) => {
792                let i = i.eval(env)?;
793                if i >= env.spent_utxos().len() {
794                    return Err(EvalError::UtxoIndexOutOfBounds(i, env.spent_utxos().len()));
795                }
796                Ok(env.spent_utxos()[i].value)
797            }
798            ValueExpr::Output(i) => {
799                let i = i.eval(env)?;
800                if i >= env.tx().output.len() {
801                    return Err(EvalError::OutputIndexOutOfBounds(i, env.tx().output.len()));
802                }
803                Ok(env.tx().output[i].value)
804            }
805        }
806    }
807
808    /// Returns (self, start_pos) parsed reversed form tokens starting with index end_pos
809    /// Expression is parsed from tokens`[start:end_pos]`
810    pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
811        let tks = tokens;
812        let e = end_pos; // short abbreviations for succinct readable code
813        if let Some(&[Tk::Bytes32(value_comm), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
814            let value = value(u8::try_from(i).ok()?, value_comm)?;
815            Some((ValueExpr::Const(CovExtArgs::Value(value)), e - 2))
816        } else if let Some(&[Tk::Bytes8(exp_val), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
817            let value = value(u8::try_from(i).ok()?, exp_val)?;
818            Some((ValueExpr::Const(CovExtArgs::Value(value)), e - 2))
819        } else if let Some(&[Tk::CurrInp, Tk::InpValue]) = tks.get(e.checked_sub(2)?..e) {
820            Some((ValueExpr::CurrInputValue, e - 2))
821        } else if let Some(&[Tk::InpValue]) = tks.get(e.checked_sub(1)?..e) {
822            let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
823            Some((ValueExpr::Input(idx_expr), e))
824        } else if let Some(&[Tk::OutValue]) = tks.get(e.checked_sub(1)?..e) {
825            let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
826            Some((ValueExpr::Output(idx_expr), e))
827        } else {
828            None
829        }
830    }
831}
832
833impl SpkExpr<CovExtArgs> {
834    /// Push this script to builder
835    pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
836        match self {
837            SpkExpr::Const(CovExtArgs::Script(s)) => {
838                let (ver, prog) = match &s.0 {
839                    SpkInner::Script(s) => spk_to_components(s),
840                    SpkInner::Hashed(h) => (-1, h.to_byte_array().to_vec()),
841                };
842                builder.push_slice(&prog).push_int(i64::from(ver))
843            }
844            SpkExpr::Const(_) => unreachable!(
845                "Both constructors from_str and from_token_iter
846            check that the correct variant is used in Script"
847            ),
848            SpkExpr::CurrInputSpk => builder
849                .push_opcode(OP_PUSHCURRENTINPUTINDEX)
850                .push_opcode(OP_INSPECTINPUTSCRIPTPUBKEY),
851            SpkExpr::Input(i) => i
852                .push_to_builder(builder)
853                .push_opcode(OP_INSPECTINPUTSCRIPTPUBKEY),
854            SpkExpr::Output(i) => i
855                .push_to_builder(builder)
856                .push_opcode(OP_INSPECTOUTPUTSCRIPTPUBKEY),
857        }
858    }
859
860    /// Evaluate this expression
861    pub fn eval(&self, env: &TxEnv) -> Result<(i8, Vec<u8>), EvalError> {
862        let res = match self {
863            SpkExpr::Const(CovExtArgs::Script(s)) => match &s.0 {
864                SpkInner::Script(s) => spk_to_components(s),
865                SpkInner::Hashed(h) => (-1, h.to_byte_array().to_vec()),
866            },
867            SpkExpr::Const(_) => unreachable!(
868                "Both constructors from_str and from_token_iter
869            check that the correct variant is used in Script pubkey"
870            ),
871            SpkExpr::CurrInputSpk => {
872                if env.idx() >= env.spent_utxos().len() {
873                    return Err(EvalError::UtxoIndexOutOfBounds(
874                        env.idx(),
875                        env.spent_utxos().len(),
876                    ));
877                }
878                spk_to_components(&env.spent_utxos()[env.idx()].script_pubkey)
879            }
880            SpkExpr::Input(i) => {
881                let i = i.eval(env)?;
882                if i >= env.spent_utxos().len() {
883                    return Err(EvalError::UtxoIndexOutOfBounds(i, env.spent_utxos().len()));
884                }
885                spk_to_components(&(env.spent_utxos()[i].script_pubkey))
886            }
887            SpkExpr::Output(i) => {
888                let i = i.eval(env)?;
889                if i >= env.tx().output.len() {
890                    return Err(EvalError::OutputIndexOutOfBounds(i, env.tx().output.len()));
891                }
892                spk_to_components(&(env.tx().output[i].script_pubkey))
893            }
894        };
895        Ok(res)
896    }
897
898    /// Returns (self, start_pos) parsed reversed form tokens starting with index end_pos
899    /// Expression is parsed from tokens`[start:end_pos]`
900    pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
901        let tks = tokens;
902        let e = end_pos; // short abbreviations for succinct readable code
903        if let Some(&[Tk::Bytes32(spk_vec), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
904            let script = spk(i8::try_from(i).ok()?, spk_vec)?;
905            Some((SpkExpr::Const(CovExtArgs::Script(Spk::new(script))), e - 2))
906        } else if let Some(&[Tk::Bytes32(spk_vec), Tk::NumNeg1]) = tks.get(e.checked_sub(2)?..e) {
907            let mut inner = [0u8; 32];
908            inner.copy_from_slice(spk_vec);
909            let hashed_spk = Spk(SpkInner::Hashed(sha256::Hash::from_byte_array(inner)));
910            Some((SpkExpr::Const(CovExtArgs::Script(hashed_spk)), e - 2))
911        } else if let Some(&[Tk::Push(ref spk_vec), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
912            let script = spk(i8::try_from(i).ok()?, spk_vec)?;
913            Some((SpkExpr::Const(CovExtArgs::Script(Spk::new(script))), e - 2))
914        } else if let Some(&[Tk::CurrInp, Tk::InpSpk]) = tks.get(e.checked_sub(2)?..e) {
915            Some((SpkExpr::CurrInputSpk, e - 2))
916        } else if let Some(&[Tk::InpSpk]) = tks.get(e.checked_sub(1)?..e) {
917            let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
918            Some((SpkExpr::Input(idx_expr), e))
919        } else if let Some(&[Tk::OutSpk]) = tks.get(e.checked_sub(1)?..e) {
920            let (idx_expr, e) = IdxExpr::from_tokens(tks, e - 1)?;
921            Some((SpkExpr::Output(idx_expr), e))
922        } else {
923            None
924        }
925    }
926}
927
928impl CovOps<CovExtArgs> {
929    /// Push this script to builder
930    pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
931        match self {
932            CovOps::IsExpAsset(x) => x
933                .push_to_builder(builder)
934                .push_int(1)
935                .push_opcode(OP_EQUAL)
936                .push_opcode(OP_NIP),
937            CovOps::IsExpValue(x) => x
938                .push_to_builder(builder)
939                .push_int(1)
940                .push_opcode(OP_EQUAL)
941                .push_opcode(OP_NIP),
942            CovOps::AssetEq(x, y) => {
943                // pushes [asset_x] [pref_x] [asset_y] [pref_y] on top. Check that both prefixes and values match.
944                let builder = x.push_to_builder(builder).push_opcode(OP_TOALTSTACK);
945                let builder = y
946                    .push_to_builder(builder)
947                    .push_opcode(OP_FROMALTSTACK)
948                    .push_opcode(OP_EQUAL);
949                builder
950                    .push_opcode(OP_TOALTSTACK)
951                    .push_opcode(OP_EQUAL)
952                    .push_opcode(OP_FROMALTSTACK)
953                    .push_opcode(OP_BOOLAND)
954            }
955            CovOps::ValueEq(x, y) => {
956                // pushes [value_x] [pref_x] [value_y] [pref_y] on top. Check that both prefixes and values match.
957                let builder = x.push_to_builder(builder).push_opcode(OP_TOALTSTACK);
958                let builder = y
959                    .push_to_builder(builder)
960                    .push_opcode(OP_FROMALTSTACK)
961                    .push_opcode(OP_EQUAL);
962                builder
963                    .push_opcode(OP_TOALTSTACK)
964                    .push_opcode(OP_EQUAL)
965                    .push_opcode(OP_FROMALTSTACK)
966                    .push_opcode(OP_BOOLAND)
967            }
968            CovOps::SpkEq(x, y) => {
969                // pushes [spk_x] [wit_ver_x] [spk_y] [wit_ver_y] on top. Check that both prefixes and values match.
970                let builder = x.push_to_builder(builder).push_opcode(OP_TOALTSTACK);
971                let builder = y
972                    .push_to_builder(builder)
973                    .push_opcode(OP_FROMALTSTACK)
974                    .push_opcode(OP_EQUAL);
975                builder
976                    .push_opcode(OP_TOALTSTACK)
977                    .push_opcode(OP_EQUAL)
978                    .push_opcode(OP_FROMALTSTACK)
979                    .push_opcode(OP_BOOLAND)
980            }
981            CovOps::CurrIndEq(i) => builder
982                .push_int(*i as i64)
983                .push_opcode(OP_PUSHCURRENTINPUTINDEX)
984                .push_opcode(OP_EQUAL),
985            CovOps::IdxEq(x, y) => {
986                // pushes [idx_x] [idx_y] on top. Check that both prefixes and values match.
987                let builder = x.push_to_builder(builder);
988                let builder = y.push_to_builder(builder);
989                builder.push_opcode(OP_EQUAL)
990            }
991        }
992    }
993
994    /// Evaluate this expression
995    pub fn eval(&self, env: &TxEnv) -> Result<bool, EvalError> {
996        match self {
997            CovOps::IsExpAsset(x) => x.eval(env).map(|x| x.is_explicit()),
998            CovOps::IsExpValue(y) => y.eval(env).map(|y| y.is_explicit()),
999            CovOps::AssetEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
1000            CovOps::ValueEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
1001            CovOps::SpkEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
1002            CovOps::CurrIndEq(i) => Ok(*i == env.idx()),
1003            CovOps::IdxEq(x, y) => Ok(x.eval(env)? == y.eval(env)?),
1004        }
1005    }
1006
1007    /// Returns (self, start_pos) parsed reversed form tokens starting with index end_pos
1008    /// Expression is parsed from tokens`[start:end_pos]`
1009    pub fn from_tokens(tks: &[Tk]) -> Option<(Self, usize)> {
1010        let e = tks.len();
1011        if let Some(&[Tk::Num(i), Tk::CurrInp, Tk::Equal]) = tks.get(e.checked_sub(3)?..e) {
1012            Some((CovOps::CurrIndEq(i as usize), e - 3))
1013        } else if let Some(&[Tk::Equal]) = tks.get(e.checked_sub(1)?..e) {
1014            let (y, e) = IdxExpr::from_tokens(tks, e - 1)?;
1015            let (x, e) = IdxExpr::from_tokens(tks, e)?;
1016            Some((CovOps::IdxEq(x, y), e))
1017        } else if let Some(&[Tk::Num(1), Tk::Equal, Tk::Nip]) = tks.get(e.checked_sub(3)?..e) {
1018            if let Some((asset, e)) = AssetExpr::from_tokens(tks, e - 3) {
1019                Some((CovOps::IsExpAsset(asset), e))
1020            } else if let Some((value, e)) = ValueExpr::from_tokens(tks, e - 3) {
1021                Some((CovOps::IsExpValue(value), e))
1022            } else {
1023                None
1024            }
1025        } else if let Some(
1026            &[Tk::FromAltStack, Tk::Equal, Tk::ToAltStack, Tk::Equal, Tk::FromAltStack, Tk::BoolAnd],
1027        ) = tks.get(e.checked_sub(6)?..e)
1028        {
1029            let res = if let Some((y, e)) = AssetExpr::from_tokens(tks, e - 6) {
1030                if tks.get(e - 1) != Some(&Tk::ToAltStack) {
1031                    return None;
1032                }
1033                if let Some((x, e)) = AssetExpr::from_tokens(tks, e - 1) {
1034                    Some((CovOps::AssetEq(x, y), e))
1035                } else {
1036                    None
1037                }
1038            } else {
1039                None
1040            };
1041            if res.is_some() {
1042                return res;
1043            }
1044            let res = if let Some((y, e)) = ValueExpr::from_tokens(tks, e - 6) {
1045                if tks.get(e - 1) != Some(&Tk::ToAltStack) {
1046                    return None;
1047                }
1048                if let Some((x, e)) = ValueExpr::from_tokens(tks, e - 1) {
1049                    Some((CovOps::ValueEq(x, y), e))
1050                } else {
1051                    None
1052                }
1053            } else {
1054                None
1055            };
1056            if res.is_some() {
1057                return res;
1058            }
1059            if let Some((y, e)) = SpkExpr::from_tokens(tks, e - 6) {
1060                if tks.get(e - 1) != Some(&Tk::ToAltStack) {
1061                    return None;
1062                }
1063                if let Some((x, e)) = SpkExpr::from_tokens(tks, e - 1) {
1064                    Some((CovOps::SpkEq(x, y), e))
1065                } else {
1066                    None
1067                }
1068            } else {
1069                None
1070            }
1071        } else {
1072            None
1073        }
1074    }
1075}
1076
1077impl ParseableExt for CovOps<CovExtArgs> {
1078    fn satisfy<Pk, S>(&self, sat: &S) -> Satisfaction
1079    where
1080        Pk: ToPublicKey,
1081        S: Satisfier<Pk>,
1082    {
1083        let env = match (
1084            sat.lookup_tx(),
1085            sat.lookup_spent_utxos(),
1086            sat.lookup_curr_inp(),
1087        ) {
1088            (Some(tx), Some(utxos), Some(idx)) => match TxEnv::new(tx, utxos, idx) {
1089                Some(x) => x,
1090                None => {
1091                    return Satisfaction {
1092                        stack: Witness::Impossible,
1093                        has_sig: false,
1094                    }
1095                }
1096            },
1097            _ => {
1098                return Satisfaction {
1099                    stack: Witness::Impossible,
1100                    has_sig: false,
1101                }
1102            }
1103        };
1104        let wit = match self.eval(&env) {
1105            Ok(false) => Witness::Unavailable,
1106            Ok(true) => Witness::empty(),
1107            Err(_e) => Witness::Impossible,
1108        };
1109        Satisfaction {
1110            stack: wit,
1111            has_sig: false,
1112        }
1113    }
1114
1115    fn dissatisfy<Pk, S>(&self, sat: &S) -> Satisfaction
1116    where
1117        Pk: ToPublicKey,
1118        S: Satisfier<Pk>,
1119    {
1120        let env = match (
1121            sat.lookup_tx(),
1122            sat.lookup_spent_utxos(),
1123            sat.lookup_curr_inp(),
1124        ) {
1125            (Some(tx), Some(utxos), Some(idx)) => match TxEnv::new(tx, utxos, idx) {
1126                Some(x) => x,
1127                None => {
1128                    return Satisfaction {
1129                        stack: Witness::Impossible,
1130                        has_sig: false,
1131                    }
1132                }
1133            },
1134            _ => {
1135                return Satisfaction {
1136                    stack: Witness::Impossible,
1137                    has_sig: false,
1138                }
1139            }
1140        };
1141        let wit = match self.eval(&env) {
1142            Ok(false) => Witness::empty(),
1143            Ok(true) => Witness::Unavailable,
1144            Err(_e) => Witness::Impossible,
1145        };
1146        Satisfaction {
1147            stack: wit,
1148            has_sig: false,
1149        }
1150    }
1151
1152    fn push_to_builder(&self, builder: elements::script::Builder) -> elements::script::Builder {
1153        self.push_to_builder(builder)
1154    }
1155
1156    fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result<Self, FromTokenIterError> {
1157        let len = tokens.len();
1158        match Self::from_tokens(tokens.as_inner_mut()) {
1159            Some((res, last_pos)) => {
1160                tokens.advance(len - last_pos).ok_or(FromTokenIterError)?;
1161                Ok(res)
1162            }
1163            None => Err(FromTokenIterError),
1164        }
1165    }
1166
1167    fn evaluate(
1168        &self,
1169        stack: &mut interpreter::Stack,
1170        txenv: Option<&TxEnv>,
1171    ) -> Result<bool, interpreter::Error> {
1172        let txenv = txenv
1173            .as_ref()
1174            .ok_or(interpreter::Error::ArithError(EvalError::TxEnvNotPresent))?;
1175
1176        match self.eval(txenv) {
1177            Ok(true) => {
1178                stack.push(interpreter::Element::Satisfied);
1179                Ok(true)
1180            }
1181            Ok(false) => {
1182                stack.push(interpreter::Element::Dissatisfied);
1183                Ok(false)
1184            }
1185            Err(e) => Err(interpreter::Error::ArithError(e)),
1186        }
1187    }
1188}
1189
1190impl<PArg, QArg> TranslateExtParam<PArg, QArg> for CovOps<PArg>
1191where
1192    PArg: ExtParam,
1193    QArg: ExtParam,
1194{
1195    type Output = CovOps<QArg>;
1196
1197    fn translate_ext<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
1198    where
1199        T: ExtParamTranslator<PArg, QArg, E>,
1200    {
1201        match self {
1202            CovOps::IsExpAsset(a) => Ok(CovOps::IsExpAsset(a._translate_ext(t)?)),
1203            CovOps::IsExpValue(v) => Ok(CovOps::IsExpValue(v._translate_ext(t)?)),
1204            CovOps::AssetEq(x, y) => {
1205                Ok(CovOps::AssetEq(x._translate_ext(t)?, y._translate_ext(t)?))
1206            }
1207            CovOps::ValueEq(x, y) => {
1208                Ok(CovOps::ValueEq(x._translate_ext(t)?, y._translate_ext(t)?))
1209            }
1210            CovOps::SpkEq(x, y) => Ok(CovOps::SpkEq(x._translate_ext(t)?, y._translate_ext(t)?)),
1211            CovOps::CurrIndEq(i) => Ok(CovOps::CurrIndEq(*i)),
1212            CovOps::IdxEq(x, y) => Ok(CovOps::IdxEq(x.clone(), y.clone())),
1213        }
1214    }
1215}
1216
1217#[cfg(test)]
1218mod tests {
1219    use bitcoin::key::XOnlyPublicKey;
1220
1221    use super::*;
1222    use crate::test_utils::{StrExtTranslator, StrXOnlyKeyTranslator};
1223    use crate::{Miniscript, Segwitv0, Tap, TranslatePk};
1224
1225    #[test]
1226    fn test_index_ops() {
1227        // index ops tests with different index types
1228        _test_parse("is_exp_asset(inp_asset(curr_idx))");
1229        _test_parse("is_exp_asset(inp_asset(idx_add(9,curr_idx)))");
1230        _test_parse("is_exp_asset(inp_asset(idx_sub(9,curr_idx)))");
1231        _test_parse("is_exp_asset(inp_asset(idx_mul(9,curr_idx)))");
1232        _test_parse("is_exp_asset(inp_asset(idx_div(9,curr_idx)))");
1233        _test_parse("is_exp_asset(inp_asset(idx_mul(1,idx_add(9,curr_idx))))");
1234        _test_parse("is_exp_asset(inp_asset(idx_sub(idx_mul(1,idx_add(9,curr_idx)),1)))");
1235
1236        // test type parent fragments
1237        _test_parse("is_exp_asset(out_asset(idx_add(9,curr_idx)))");
1238        _test_parse("is_exp_value(inp_value(idx_add(9,curr_idx)))");
1239        _test_parse("is_exp_value(out_value(idx_add(9,curr_idx)))");
1240        _test_parse("spk_eq(inp_spk(idx_add(9,curr_idx)),out_spk(idx_sub(9,curr_idx)))");
1241
1242        _test_parse("idx_eq(10,idx_add(9,curr_idx))");
1243    }
1244
1245    #[test]
1246    fn cov_parse() {
1247        // This does not test the evaluation
1248        _test_parse("is_exp_asset(ConfAst)");
1249        _test_parse("is_exp_asset(ExpAst)");
1250        _test_parse("is_exp_asset(curr_inp_asset)");
1251        _test_parse("is_exp_asset(inp_asset(9))");
1252        _test_parse("is_exp_asset(out_asset(9))");
1253        _test_parse("asset_eq(ConfAst,ExpAst)");
1254        _test_parse("asset_eq(curr_inp_asset,out_asset(1))");
1255        _test_parse("asset_eq(inp_asset(3),out_asset(1))");
1256
1257        // same tests for values
1258        _test_parse("is_exp_value(ConfVal)");
1259        _test_parse("is_exp_value(ExpVal)");
1260        _test_parse("is_exp_value(curr_inp_value)");
1261        _test_parse("is_exp_value(inp_value(9))");
1262        _test_parse("is_exp_value(out_value(9))");
1263        _test_parse("value_eq(ConfVal,ExpVal)");
1264        _test_parse("value_eq(curr_inp_value,out_value(1))");
1265        _test_parse("value_eq(inp_value(3),out_value(1))");
1266
1267        // same tests for spks
1268        _test_parse("spk_eq(V0Spk,out_spk(1))");
1269        _test_parse("spk_eq(V1Spk,inp_spk(1))");
1270        _test_parse("spk_eq(curr_inp_spk,out_spk(1))");
1271        _test_parse("spk_eq(inp_spk(3),out_spk(1))");
1272        _test_parse("spk_eq(out_spk(2),V1Spk)");
1273
1274        // Testing the current input index
1275        _test_parse("curr_idx_eq(1)");
1276        _test_parse("curr_idx_eq(0)");
1277
1278        // test some misc combinations with other miniscript fragments
1279        _test_parse(
1280            "and_v(v:pk(K),and_v(v:is_exp_value(out_value(1)),is_exp_asset(out_asset(1))))",
1281        );
1282        _test_parse("and_v(v:pk(K),and_v(v:value_eq(ConfVal,ConfVal),spk_eq(V1Spk,V1Spk)))");
1283        _test_parse("and_v(v:pk(K),and_v(v:value_eq(ConfVal,ConfVal),and_v(v:spk_eq(V1Spk,V1Spk),curr_idx_eq(1))))");
1284    }
1285
1286    #[test]
1287    fn options_fail_test() {
1288        type MsExt = Miniscript<XOnlyPublicKey, Tap, CovOps<CovExtArgs>>;
1289
1290        // 33 bytes explicit asset succeeds
1291        MsExt::from_str_insane("asset_eq(out_asset(0),0179d51a47e4ac8e32306486dd0926a88678c392f2ed5f213e3ff2ad461c7c25e1)").unwrap();
1292        // 32 bytes explicit asset without prefix fails
1293        MsExt::from_str_insane("asset_eq(out_asset(0),79d51a47e4ac8e32306486dd0926a88678c392f2ed5f213e3ff2ad461c7c25e1)").unwrap_err();
1294    }
1295
1296    #[rustfmt::skip]
1297    fn _test_parse(s: &str) {
1298        type MsExtStr = Miniscript<String, Tap, CovOps<String>>;
1299        type MsExt = Miniscript<XOnlyPublicKey, Tap, CovOps<CovExtArgs>>;
1300        type MsExtSegwitv0 = Miniscript<String, Segwitv0, CovOps<CovExtArgs>>;
1301
1302        // Make sure that parsing this errors in segwit context
1303        assert!(MsExtSegwitv0::from_str_insane(s).is_err());
1304
1305        let ms = MsExtStr::from_str_insane(s).unwrap();
1306        // test string rtt
1307        assert_eq!(ms.to_string(), s);
1308        let mut t = StrXOnlyKeyTranslator::default();
1309        let mut ext_t = StrExtTranslator::default();
1310        {
1311            ext_t.ext_map.insert("V1Spk".to_string(),CovExtArgs::spk(elements::Script::from_hex_no_prefix("5120c73ac1b7a518499b9642aed8cfa15d5401e5bd85ad760b937b69521c297722f0").unwrap()));
1312            ext_t.ext_map.insert("V0Spk".to_string(),CovExtArgs::spk(elements::Script::from_hex_no_prefix("0020c73ac1b7a518499b9642aed8cfa15d5401e5bd85ad760b937b69521c297722f0").unwrap()));
1313            ext_t.ext_map.insert("ConfAst".to_string(),CovExtArgs::asset(encode::deserialize(&Vec::<u8>::from_hex("0adef814ab021498562ab4717287305d3f7abb5686832fe6183e1db495abef7cc7").unwrap()).unwrap()));
1314            ext_t.ext_map.insert("ExpAst".to_string(),CovExtArgs::asset(encode::deserialize(&Vec::<u8>::from_hex("01c73ac1b7a518499b9642aed8cfa15d5401e5bd85ad760b937b69521c297722f0").unwrap()).unwrap()));
1315            ext_t.ext_map.insert("ConfVal".to_string(),CovExtArgs::value(encode::deserialize(&Vec::<u8>::from_hex("09def814ab021498562ab4717287305d3f7abb5686832fe6183e1db495abef7cc7").unwrap()).unwrap()));
1316            ext_t.ext_map.insert("ExpVal".to_string(),CovExtArgs::value(encode::deserialize(&Vec::<u8>::from_hex("010000000011110000").unwrap()).unwrap()));
1317        }
1318        let ms: Miniscript<XOnlyPublicKey, Tap, CovOps<String>> = ms.translate_pk(&mut t).unwrap();
1319        let ms: Miniscript<XOnlyPublicKey, Tap, CovOps<CovExtArgs>> = ms.translate_ext(&mut ext_t).unwrap();
1320        // script rtt
1321        assert_eq!(ms.encode(), MsExt::parse_insane(&ms.encode()).unwrap().encode());
1322        // String rtt of the translated script
1323        assert_eq!(ms, MsExt::from_str_insane(&ms.to_string()).unwrap())
1324    }
1325}