1use 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#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
34pub enum AssetExpr<T: ExtParam> {
35 Const(T),
39 CurrInputAsset,
42 Input(IdxExpr),
45 Output(IdxExpr),
48}
49
50#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
56pub enum ValueExpr<T: ExtParam> {
57 Const(T),
60 CurrInputValue,
63 Input(IdxExpr),
66 Output(IdxExpr),
69}
70
71#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
78pub enum SpkExpr<T: ExtParam> {
79 Const(T),
84 CurrInputSpk,
87 Input(IdxExpr),
90 Output(IdxExpr),
93}
94
95#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
100pub enum CovOps<T: ExtParam> {
101 IsExpAsset(AssetExpr<T>),
104 IsExpValue(ValueExpr<T>),
109 AssetEq(AssetExpr<T>, AssetExpr<T>),
112 ValueEq(ValueExpr<T>, ValueExpr<T>),
115 SpkEq(SpkExpr<T>, SpkExpr<T>),
119 CurrIndEq(usize),
122 IdxEq(IdxExpr, IdxExpr),
125}
126
127impl<T: ExtParam> AssetExpr<T> {
128 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 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 fn script_size(&self) -> usize {
202 match self {
203 ValueExpr::Const(_c) => 33 + 1, ValueExpr::CurrInputValue => 2,
205 ValueExpr::Input(i) => i.script_size() + 1,
206 ValueExpr::Output(i) => i.script_size() + 1,
207 }
208 }
209
210 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 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 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, dissatisfiable: false, unit: true,
429 }
430 }
431
432 fn mall_prop(&self) -> Malleability {
433 Malleability {
434 dissat: Dissat::None, safe: false, non_malleable: true, }
438 }
439
440 fn extra_prop(&self) -> ExtData {
441 ExtData {
442 pk_cost: self.script_size(), 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), exec_stack_elem_count_dissat: Some(4),
451 ops: OpLimits {
452 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(), };
479 Self::from_tree(&tree).map_err(|_| FromTokenIterError)
480 }
481
482 fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> {
483 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
507impl<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 fn ext(&mut self, cov_ops: &CovOps<PArg>) -> Result<CovOps<QArg>, E> {
516 TranslateExtParam::translate_ext(cov_ops, self)
517 }
518}
519
520#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
523pub struct Spk(SpkInner);
524
525impl Spk {
526 pub fn new(s: elements::Script) -> Self {
528 Spk(SpkInner::Script(s))
529 }
530}
531
532#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
534pub enum SpkInner {
535 Script(elements::Script),
537 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"), }
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
587fn 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
598fn 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
618fn spk(pref: i8, prog: &[u8]) -> Option<elements::Script> {
620 if pref == -1 {
621 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
636fn 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 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 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), 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 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 pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
723 let tks = tokens;
724 let e = end_pos; 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 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 } confidential::Value::Explicit(a) => {
752 builder.push_slice(&a.to_le_bytes()).push_int(1)
753 } 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 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 pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
811 let tks = tokens;
812 let e = end_pos; 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 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 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 pub fn from_tokens(tokens: &[Tk], end_pos: usize) -> Option<(Self, usize)> {
901 let tks = tokens;
902 let e = end_pos; 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 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 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 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 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 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 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 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 _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_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 _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 _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 _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 _test_parse("curr_idx_eq(1)");
1276 _test_parse("curr_idx_eq(0)");
1277
1278 _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 MsExt::from_str_insane("asset_eq(out_asset(0),0179d51a47e4ac8e32306486dd0926a88678c392f2ed5f213e3ff2ad461c7c25e1)").unwrap();
1292 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 assert!(MsExtSegwitv0::from_str_insane(s).is_err());
1304
1305 let ms = MsExtStr::from_str_insane(s).unwrap();
1306 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 assert_eq!(ms.encode(), MsExt::parse_insane(&ms.encode()).unwrap().encode());
1322 assert_eq!(ms, MsExt::from_str_insane(&ms.to_string()).unwrap())
1324 }
1325}