|
7 | 7 | //! encoding in Bitcoin script, as well as a datatype. Full details
|
8 | 8 | //! are given on the Miniscript website.
|
9 | 9 |
|
10 |
| -use core::fmt; |
11 | 10 | use core::str::FromStr;
|
12 | 11 |
|
13 | 12 | use bitcoin::hashes::{hash160, Hash};
|
14 | 13 | use bitcoin::{absolute, opcodes, script};
|
15 | 14 | use sync::Arc;
|
16 | 15 |
|
17 | 16 | use crate::miniscript::context::SigType;
|
18 |
| -use crate::miniscript::{types, ScriptContext}; |
| 17 | +use crate::miniscript::ScriptContext; |
19 | 18 | use crate::prelude::*;
|
20 | 19 | use crate::util::MsKeyBuilder;
|
21 | 20 | use crate::{
|
22 | 21 | expression, AbsLockTime, Error, FromStrKey, Miniscript, MiniscriptKey, RelLockTime, Terminal,
|
23 | 22 | ToPublicKey,
|
24 | 23 | };
|
25 | 24 |
|
26 |
| -impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> { |
27 |
| - /// Internal helper function for displaying wrapper types; returns |
28 |
| - /// a character to display before the `:` as well as a reference |
29 |
| - /// to the wrapped type to allow easy recursion |
30 |
| - fn wrap_char(&self) -> Option<(char, &Arc<Miniscript<Pk, Ctx>>)> { |
31 |
| - match *self { |
32 |
| - Terminal::Alt(ref sub) => Some(('a', sub)), |
33 |
| - Terminal::Swap(ref sub) => Some(('s', sub)), |
34 |
| - Terminal::Check(ref sub) => Some(('c', sub)), |
35 |
| - Terminal::DupIf(ref sub) => Some(('d', sub)), |
36 |
| - Terminal::Verify(ref sub) => Some(('v', sub)), |
37 |
| - Terminal::NonZero(ref sub) => Some(('j', sub)), |
38 |
| - Terminal::ZeroNotEqual(ref sub) => Some(('n', sub)), |
39 |
| - Terminal::AndV(ref sub, ref r) if r.node == Terminal::True => Some(('t', sub)), |
40 |
| - Terminal::OrI(ref sub, ref r) if r.node == Terminal::False => Some(('u', sub)), |
41 |
| - Terminal::OrI(ref l, ref sub) if l.node == Terminal::False => Some(('l', sub)), |
42 |
| - _ => None, |
43 |
| - } |
44 |
| - } |
45 |
| - |
46 |
| - fn conditional_fmt(&self, f: &mut fmt::Formatter, is_debug: bool) -> fmt::Result { |
47 |
| - match *self { |
48 |
| - Terminal::PkK(ref pk) => fmt_1(f, "pk_k(", pk, is_debug), |
49 |
| - Terminal::PkH(ref pk) => fmt_1(f, "pk_h(", pk, is_debug), |
50 |
| - Terminal::RawPkH(ref pkh) => fmt_1(f, "expr_raw_pk_h(", pkh, is_debug), |
51 |
| - Terminal::After(ref t) => fmt_1(f, "after(", t, is_debug), |
52 |
| - Terminal::Older(ref t) => fmt_1(f, "older(", t, is_debug), |
53 |
| - Terminal::Sha256(ref h) => fmt_1(f, "sha256(", h, is_debug), |
54 |
| - Terminal::Hash256(ref h) => fmt_1(f, "hash256(", h, is_debug), |
55 |
| - Terminal::Ripemd160(ref h) => fmt_1(f, "ripemd160(", h, is_debug), |
56 |
| - Terminal::Hash160(ref h) => fmt_1(f, "hash160(", h, is_debug), |
57 |
| - Terminal::True => f.write_str("1"), |
58 |
| - Terminal::False => f.write_str("0"), |
59 |
| - Terminal::AndV(ref l, ref r) if r.node != Terminal::True => { |
60 |
| - fmt_2(f, "and_v(", l, r, is_debug) |
61 |
| - } |
62 |
| - Terminal::AndB(ref l, ref r) => fmt_2(f, "and_b(", l, r, is_debug), |
63 |
| - Terminal::AndOr(ref a, ref b, ref c) => { |
64 |
| - if c.node == Terminal::False { |
65 |
| - fmt_2(f, "and_b(", a, b, is_debug) |
66 |
| - } else { |
67 |
| - f.write_str("andor(")?; |
68 |
| - conditional_fmt(f, a, is_debug)?; |
69 |
| - f.write_str(",")?; |
70 |
| - conditional_fmt(f, b, is_debug)?; |
71 |
| - f.write_str(",")?; |
72 |
| - conditional_fmt(f, c, is_debug)?; |
73 |
| - f.write_str(")") |
74 |
| - } |
75 |
| - } |
76 |
| - Terminal::OrB(ref l, ref r) => fmt_2(f, "or_b(", l, r, is_debug), |
77 |
| - Terminal::OrD(ref l, ref r) => fmt_2(f, "or_d(", l, r, is_debug), |
78 |
| - Terminal::OrC(ref l, ref r) => fmt_2(f, "or_c(", l, r, is_debug), |
79 |
| - Terminal::OrI(ref l, ref r) |
80 |
| - if l.node != Terminal::False && r.node != Terminal::False => |
81 |
| - { |
82 |
| - fmt_2(f, "or_i(", l, r, is_debug) |
83 |
| - } |
84 |
| - Terminal::Thresh(ref thresh) => { |
85 |
| - if is_debug { |
86 |
| - fmt::Debug::fmt(&thresh.debug("thresh", true), f) |
87 |
| - } else { |
88 |
| - fmt::Display::fmt(&thresh.display("thresh", true), f) |
89 |
| - } |
90 |
| - } |
91 |
| - Terminal::Multi(ref thresh) => { |
92 |
| - if is_debug { |
93 |
| - fmt::Debug::fmt(&thresh.debug("multi", true), f) |
94 |
| - } else { |
95 |
| - fmt::Display::fmt(&thresh.display("multi", true), f) |
96 |
| - } |
97 |
| - } |
98 |
| - Terminal::MultiA(ref thresh) => { |
99 |
| - if is_debug { |
100 |
| - fmt::Debug::fmt(&thresh.debug("multi_a", true), f) |
101 |
| - } else { |
102 |
| - fmt::Display::fmt(&thresh.display("multi_a", true), f) |
103 |
| - } |
104 |
| - } |
105 |
| - // wrappers |
106 |
| - _ => { |
107 |
| - if let Some((ch, sub)) = self.wrap_char() { |
108 |
| - if ch == 'c' { |
109 |
| - if let Terminal::PkK(ref pk) = sub.node { |
110 |
| - // alias: pk(K) = c:pk_k(K) |
111 |
| - return fmt_1(f, "pk(", pk, is_debug); |
112 |
| - } else if let Terminal::RawPkH(ref pkh) = sub.node { |
113 |
| - // `RawPkH` is currently unsupported in the descriptor spec |
114 |
| - // alias: pkh(K) = c:pk_h(K) |
115 |
| - // We temporarily display there using raw_pkh, but these descriptors |
116 |
| - // are not defined in the spec yet. These are prefixed with `expr` |
117 |
| - // in the descriptor string. |
118 |
| - // We do not support parsing these descriptors yet. |
119 |
| - return fmt_1(f, "expr_raw_pkh(", pkh, is_debug); |
120 |
| - } else if let Terminal::PkH(ref pk) = sub.node { |
121 |
| - // alias: pkh(K) = c:pk_h(K) |
122 |
| - return fmt_1(f, "pkh(", pk, is_debug); |
123 |
| - } |
124 |
| - } |
125 |
| - |
126 |
| - fmt::Write::write_char(f, ch)?; |
127 |
| - match sub.node.wrap_char() { |
128 |
| - None => { |
129 |
| - f.write_str(":")?; |
130 |
| - } |
131 |
| - // Add a ':' wrapper if there are other wrappers apart from c:pk_k() |
132 |
| - // tvc:pk_k() -> tv:pk() |
133 |
| - Some(('c', ms)) => match ms.node { |
134 |
| - Terminal::PkK(_) | Terminal::PkH(_) | Terminal::RawPkH(_) => { |
135 |
| - f.write_str(":")?; |
136 |
| - } |
137 |
| - _ => {} |
138 |
| - }, |
139 |
| - _ => {} |
140 |
| - }; |
141 |
| - if is_debug { |
142 |
| - write!(f, "{:?}", sub) |
143 |
| - } else { |
144 |
| - write!(f, "{}", sub) |
145 |
| - } |
146 |
| - } else { |
147 |
| - unreachable!(); |
148 |
| - } |
149 |
| - } |
150 |
| - } |
151 |
| - } |
152 |
| -} |
153 |
| - |
154 |
| -fn fmt_1<D: fmt::Debug + fmt::Display>( |
155 |
| - f: &mut fmt::Formatter, |
156 |
| - name: &str, |
157 |
| - a: &D, |
158 |
| - is_debug: bool, |
159 |
| -) -> fmt::Result { |
160 |
| - f.write_str(name)?; |
161 |
| - conditional_fmt(f, a, is_debug)?; |
162 |
| - f.write_str(")") |
163 |
| -} |
164 |
| -fn fmt_2<D: fmt::Debug + fmt::Display>( |
165 |
| - f: &mut fmt::Formatter, |
166 |
| - name: &str, |
167 |
| - a: &D, |
168 |
| - b: &D, |
169 |
| - is_debug: bool, |
170 |
| -) -> fmt::Result { |
171 |
| - f.write_str(name)?; |
172 |
| - conditional_fmt(f, a, is_debug)?; |
173 |
| - f.write_str(",")?; |
174 |
| - conditional_fmt(f, b, is_debug)?; |
175 |
| - f.write_str(")") |
176 |
| -} |
177 |
| -fn conditional_fmt<D: fmt::Debug + fmt::Display>( |
178 |
| - f: &mut fmt::Formatter, |
179 |
| - data: &D, |
180 |
| - is_debug: bool, |
181 |
| -) -> fmt::Result { |
182 |
| - if is_debug { |
183 |
| - fmt::Debug::fmt(data, f) |
184 |
| - } else { |
185 |
| - fmt::Display::fmt(data, f) |
186 |
| - } |
187 |
| -} |
188 |
| - |
189 |
| -impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for Terminal<Pk, Ctx> { |
190 |
| - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
191 |
| - fn fmt_type_map(f: &mut fmt::Formatter<'_>, type_map: types::Type) -> fmt::Result { |
192 |
| - f.write_str(match type_map.corr.base { |
193 |
| - types::Base::B => "B", |
194 |
| - types::Base::K => "K", |
195 |
| - types::Base::V => "V", |
196 |
| - types::Base::W => "W", |
197 |
| - })?; |
198 |
| - f.write_str("/")?; |
199 |
| - f.write_str(match type_map.corr.input { |
200 |
| - types::Input::Zero => "z", |
201 |
| - types::Input::One => "o", |
202 |
| - types::Input::OneNonZero => "on", |
203 |
| - types::Input::Any => "", |
204 |
| - types::Input::AnyNonZero => "n", |
205 |
| - })?; |
206 |
| - if type_map.corr.dissatisfiable { |
207 |
| - f.write_str("d")?; |
208 |
| - } |
209 |
| - if type_map.corr.unit { |
210 |
| - f.write_str("u")?; |
211 |
| - } |
212 |
| - f.write_str(match type_map.mall.dissat { |
213 |
| - types::Dissat::None => "f", |
214 |
| - types::Dissat::Unique => "e", |
215 |
| - types::Dissat::Unknown => "", |
216 |
| - })?; |
217 |
| - if type_map.mall.safe { |
218 |
| - f.write_str("s")?; |
219 |
| - } |
220 |
| - if type_map.mall.non_malleable { |
221 |
| - f.write_str("m")?; |
222 |
| - } |
223 |
| - Ok(()) |
224 |
| - } |
225 |
| - |
226 |
| - f.write_str("[")?; |
227 |
| - if let Ok(type_map) = types::Type::type_check(self) { |
228 |
| - fmt_type_map(f, type_map)?; |
229 |
| - } else { |
230 |
| - f.write_str("TYPECHECK FAILED")?; |
231 |
| - } |
232 |
| - f.write_str("]")?; |
233 |
| - |
234 |
| - self.conditional_fmt(f, true) |
235 |
| - } |
236 |
| -} |
237 |
| - |
238 |
| -impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> { |
239 |
| - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.conditional_fmt(f, false) } |
240 |
| -} |
241 |
| - |
242 | 25 | impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Arc<Terminal<Pk, Ctx>> {
|
243 | 26 | fn from_tree(top: &expression::Tree) -> Result<Arc<Terminal<Pk, Ctx>>, Error> {
|
244 | 27 | Ok(Arc::new(expression::FromTree::from_tree(top)?))
|
|
0 commit comments