elements_miniscript/extensions/
param.rs

1//! Parameters to certain covenants
2
3use std::{fmt, hash};
4
5use elements::confidential;
6use elements::encode::serialize;
7use elements::hex::ToHex;
8
9use super::csfs::{CsfsKey, CsfsMsg};
10use super::introspect_ops::Spk;
11use super::CovenantExt;
12use crate::{Error, ExtTranslator};
13
14/// Trait for parsing extension arg from String
15/// Parse an argument from `s` given context of parent and argument position
16///
17/// When parsing all allowed parameters from string, we need to restrict where
18/// the parameters can be allowed. For example, csfs() should not have a txout
19/// parameter.
20///
21/// All parameters that should be parsed from extensions need to implement this
22pub trait ArgFromStr: Sized {
23    /// Parse an argument from `s` given context of parent and argument position
24    fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error>;
25}
26
27/// Abstract parameter to Miniscript Extension
28pub trait ExtParam: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash + ArgFromStr {}
29
30impl<T> ExtParam for T where
31    T: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash + ArgFromStr
32{
33}
34
35impl ArgFromStr for String {
36    fn arg_from_str(s: &str, _parent: &str, _pos: usize) -> Result<Self, Error> {
37        // Abstract strings are parsed without context as they don't contain any concrete
38        // information
39        Ok(String::from(s))
40    }
41}
42
43/// No Extensions for elements-miniscript
44/// All the implementations for the this function are unreachable
45#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
46pub enum NoExtParam {}
47
48impl fmt::Display for NoExtParam {
49    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match *self {}
51    }
52}
53
54impl ArgFromStr for NoExtParam {
55    fn arg_from_str(_s: &str, _parent: &str, _pos: usize) -> Result<Self, Error> {
56        // This will be removed in a followup commit
57        unreachable!("Called ArgFromStr for NoExt")
58    }
59}
60
61/// All known Extension parameters/arguments
62#[derive(Debug, PartialEq, Eq, Clone, Hash)]
63pub enum CovExtArgs {
64    /// XOnlyPublicKey (in CSFS)
65    XOnlyKey(CsfsKey),
66    /// Message
67    CsfsMsg(CsfsMsg),
68    /// Asset
69    Asset(confidential::Asset),
70    /// Value
71    Value(confidential::Value),
72    /// Script
73    Script(Spk),
74}
75
76impl From<CsfsMsg> for CovExtArgs {
77    fn from(v: CsfsMsg) -> Self {
78        Self::CsfsMsg(v)
79    }
80}
81
82impl From<Spk> for CovExtArgs {
83    fn from(v: Spk) -> Self {
84        Self::Script(v)
85    }
86}
87
88impl From<confidential::Value> for CovExtArgs {
89    fn from(v: confidential::Value) -> Self {
90        Self::Value(v)
91    }
92}
93
94impl From<confidential::Asset> for CovExtArgs {
95    fn from(v: confidential::Asset) -> Self {
96        Self::Asset(v)
97    }
98}
99
100impl From<CsfsKey> for CovExtArgs {
101    fn from(v: CsfsKey) -> Self {
102        Self::XOnlyKey(v)
103    }
104}
105
106impl CovExtArgs {
107    /// Creates a new csfs key variant of [`CovExtArgs`]
108    pub fn csfs_key(key: bitcoin::key::XOnlyPublicKey) -> Self {
109        CovExtArgs::XOnlyKey(CsfsKey(key))
110    }
111
112    /// Creates a csfs message variant of [`CovExtArgs`]
113    pub fn csfs_msg(msg: elements::secp256k1_zkp::Message) -> Self {
114        CovExtArgs::CsfsMsg(CsfsMsg::new(msg.as_ref().to_vec()).expect("32 byte size message"))
115    }
116
117    /// Creates a new asset variant of [`CovExtArgs`]
118    pub fn asset(asset: confidential::Asset) -> Self {
119        Self::from(asset)
120    }
121
122    /// Creates a new value variant of [`CovExtArgs`]
123    pub fn value(value: confidential::Value) -> Self {
124        Self::from(value)
125    }
126
127    /// Creates a new script pubkey of [`CovExtArgs`]
128    pub fn spk(spk: elements::Script) -> Self {
129        Self::from(Spk::new(spk))
130    }
131}
132
133impl PartialOrd for CovExtArgs {
134    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
135        Some(self.cmp(other))
136    }
137}
138
139impl Ord for CovExtArgs {
140    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
141        // HACKY implementation, need Ord/PartialOrd to make it work with other components
142        // in the library
143        self.to_string().cmp(&other.to_string())
144    }
145}
146
147impl fmt::Display for CovExtArgs {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        match self {
150            CovExtArgs::XOnlyKey(x) => write!(f, "{}", x),
151            CovExtArgs::CsfsMsg(m) => write!(f, "{}", m),
152            CovExtArgs::Asset(a) => write!(f, "{}", serialize(a).to_hex()),
153            CovExtArgs::Value(v) => write!(f, "{}", serialize(v).to_hex()),
154            CovExtArgs::Script(s) => write!(f, "{}", s),
155        }
156    }
157}
158
159impl ArgFromStr for CovExtArgs {
160    fn arg_from_str(s: &str, parent: &str, pos: usize) -> Result<Self, Error> {
161        let arg = match (parent, pos) {
162            ("csfs", 0) => CovExtArgs::XOnlyKey(CsfsKey::arg_from_str(s, parent, pos)?),
163            ("csfs", 1) => CovExtArgs::CsfsMsg(CsfsMsg::arg_from_str(s, parent, pos)?),
164            ("asset_eq", 0) | ("asset_eq", 1) | ("is_exp_asset", 0) => {
165                CovExtArgs::Asset(confidential::Asset::arg_from_str(s, parent, pos)?)
166            }
167            ("value_eq", 0) | ("value_eq", 1) | ("is_exp_value", 0) => {
168                CovExtArgs::Value(confidential::Value::arg_from_str(s, parent, pos)?)
169            }
170            ("spk_eq", 0) | ("spk_eq", 1) => CovExtArgs::Script(Spk::arg_from_str(s, parent, pos)?),
171            _ => return Err(Error::Unexpected(s.to_string())),
172        };
173        Ok(arg)
174    }
175}
176
177/// Trait for translating different parameter types for covenant extensions
178pub trait ExtParamTranslator<PArg, QArg, E>
179where
180    PArg: ExtParam,
181    QArg: ExtParam,
182{
183    /// Translates one extension to another
184    fn ext(&mut self, e: &PArg) -> Result<QArg, E>;
185}
186
187// Use ExtParamTranslator as a ExTTranslator
188impl<T, PArg, QArg, E> ExtTranslator<CovenantExt<PArg>, CovenantExt<QArg>, E> for T
189where
190    T: ExtParamTranslator<PArg, QArg, E>,
191    PArg: ExtParam,
192    QArg: ExtParam,
193{
194    /// Translates one extension to another
195    fn ext(&mut self, cov: &CovenantExt<PArg>) -> Result<CovenantExt<QArg>, E> {
196        match *cov {
197            CovenantExt::LegacyVerEq(ref v) => Ok(CovenantExt::LegacyVerEq(*v)),
198            CovenantExt::LegacyOutputsPref(ref p) => Ok(CovenantExt::LegacyOutputsPref(p.clone())),
199            CovenantExt::Csfs(ref c) => Ok(CovenantExt::Csfs(TranslateExtParam::translate_ext(
200                c, self,
201            )?)),
202            CovenantExt::Arith(ref e) => Ok(CovenantExt::Arith(TranslateExtParam::translate_ext(
203                e, self,
204            )?)),
205            CovenantExt::Introspect(ref c) => Ok(CovenantExt::Introspect(
206                TranslateExtParam::translate_ext(c, self)?,
207            )),
208        }
209    }
210}
211
212/// Converts a descriptor using abstract extension parameters to one using concrete ones,
213/// or vice-versa
214pub trait TranslateExtParam<PArg, QArg>
215where
216    PArg: ExtParam,
217    QArg: ExtParam,
218{
219    /// The associated output type.
220    type Output;
221
222    /// Translates a struct from one generic to another where the translations
223    /// for Pk are provided by the given [`ExtParamTranslator`].
224    fn translate_ext<T, E>(&self, translator: &mut T) -> Result<Self::Output, E>
225    where
226        T: ExtParamTranslator<PArg, QArg, E>;
227}